删除库存和旧回调管理

This commit is contained in:
yml
2026-05-25 23:07:19 +08:00
parent 90ad866ecc
commit 2c7464b1eb
87 changed files with 94 additions and 5794 deletions
@@ -1,12 +1,9 @@
import { getTaskById } from '../../repositories/task-repo.js'
import { listTaskInventoryBindingSummariesByTaskIds } from '../../repositories/task-inventory-binding-repo.js'
import { createHttpError } from '../../utils/http.js'
import { safeParseJson } from './admin-query-utils.js'
import {
createAdminViewerContext,
getTaskPrimaryClaimTokenId,
getTaskPrimaryInventoryItemId,
parseTaskContext,
} from './admin-read-shared-helpers.js'
import type {
@@ -16,8 +13,6 @@ import type {
import type { AdminTaskActionPayload } from '../../types/admin-write-models.js'
import type {
TaskEventRow,
TaskInventoryBindingRow,
TaskInventoryBindingSummaryRow,
TaskRow,
} from '../../types/repository-rows.js'
import type { AdminViewerContext } from './admin-read-shared-helpers.js'
@@ -29,10 +24,6 @@ type TaskBindingState = {
userBindingStatus: string
}
type TaskInventoryBindingLike = TaskInventoryBindingRow & {
inventory_item_status?: string
}
type OrderBindingSummary = {
totalTaskCount: number
systemBoundTaskCount: number
@@ -72,7 +63,6 @@ export function mapAdminTaskSummary(
}
export function mapTaskActionPayload(task: any): AdminTaskActionPayload {
const inventoryItemId = getTaskPrimaryInventoryItemId(task)
const primaryClaimTokenId = getTaskPrimaryClaimTokenId(task)
return {
@@ -82,7 +72,6 @@ export function mapTaskActionPayload(task: any): AdminTaskActionPayload {
deliveryStatus: task.delivery_status,
resultCode: task.result_code,
resultMessage: task.result_message,
inventoryItemId,
primaryClaimTokenId,
lastError: task.last_error,
updatedAt: task.updated_at,
@@ -104,10 +93,10 @@ export async function getRequiredTask(taskId: number | string): Promise<TaskRow>
export function mapAdminTaskListItem(
task: TaskRow,
bindingSummary: AdminTaskBindingSummary = createEmptyTaskBindingSummary(),
viewerContext: AdminViewerContext = createAdminViewerContext(),
): AdminTaskListItem {
const binding = buildTaskBindingState(task)
const bindingSummary = createEmptyTaskBindingSummary()
return {
taskId: task.id,
@@ -134,8 +123,6 @@ export function mapAdminTaskListItem(
lastError: task.last_error,
createdAt: task.created_at,
updatedAt: task.updated_at,
inventoryDisplayMasked: viewerContext.canViewSensitiveTaskData ? maskCode(task.primary_inventory_display_value) : '',
inventoryCredentialType: String(task.primary_inventory_credential_type || '').trim(),
claimToken: viewerContext.canViewSensitiveTaskData ? (task.primary_claim_token || task.claim_token || '') : '',
screenshotPath: viewerContext.role === 'support' ? '' : (task.screenshot_path || ''),
}
@@ -162,92 +149,18 @@ export function createEmptyTaskBindingSummary(): AdminTaskBindingSummary {
}
}
export async function getTaskBindingSummaryMap(taskIds: Array<number | string> = []): Promise<Map<number, AdminTaskBindingSummary>> {
const rows = await listTaskInventoryBindingSummariesByTaskIds(taskIds)
const output = new Map<number, AdminTaskBindingSummary>()
for (const row of rows as TaskInventoryBindingSummaryRow[]) {
output.set(Number(row.task_id), {
totalBindingCount: Number(row.total_binding_count || 0),
reservedBindingCount: Number(row.reserved_binding_count || 0),
consumedBindingCount: Number(row.consumed_binding_count || 0),
releasedBindingCount: Number(row.released_binding_count || 0),
roleKeys: Array.isArray(row.role_keys)
? row.role_keys.map((value) => String(value || '').trim()).filter(Boolean)
: [],
})
}
return output
}
export function getTaskBindingSummary(
summaryMap: Map<number, AdminTaskBindingSummary>,
taskId: number | string,
): AdminTaskBindingSummary {
return summaryMap.get(Number(taskId)) || createEmptyTaskBindingSummary()
}
export function createTaskBindingSummaryFromBindings(
bindings: Array<Partial<TaskInventoryBindingRow>> | null | undefined = [],
): AdminTaskBindingSummary {
const normalizedBindings = Array.isArray(bindings) ? bindings : []
const roleKeys = Array.from(new Set(normalizedBindings
.map((binding) => String(binding?.role_key || '').trim())
.filter(Boolean)))
return {
totalBindingCount: normalizedBindings.length,
reservedBindingCount: normalizedBindings.filter((binding) => String(binding?.binding_status || '') === 'reserved').length,
consumedBindingCount: normalizedBindings.filter((binding) => String(binding?.binding_status || '') === 'consumed').length,
releasedBindingCount: normalizedBindings.filter((binding) => String(binding?.binding_status || '') === 'released').length,
roleKeys,
}
}
export function mapAdminTaskInventoryBinding(
binding: TaskInventoryBindingLike,
task: TaskRow,
viewerContext: AdminViewerContext = createAdminViewerContext(),
): JsonRecord {
const metadata = safeParseJson(binding.metadata_json)
return {
bindingId: binding.id,
inventoryItemId: binding.inventory_item_id,
roleKey: String(binding.role_key || '').trim(),
quantity: Math.max(1, Number(binding.quantity || 1)),
bindingStatus: String(binding.binding_status || '').trim(),
inventoryStatus: String(binding.inventory_item_status || '').trim(),
skuCode: String(binding.sku_code || '').trim(),
batchNo: String(binding.batch_no || '').trim(),
credentialType: String(binding.credential_type || 'claim_code').trim() || 'claim_code',
displayValue: viewerContext.canViewSensitiveTaskData ? String(binding.display_value || '').trim() : '',
invalidReason: String(binding.invalid_reason || '').trim(),
consumedAt: binding.consumed_at || null,
releasedAt: binding.released_at || null,
createdAt: binding.created_at,
updatedAt: binding.updated_at,
metadata: normalizeRecord(metadata),
isPrimary: Number(binding.inventory_item_id || 0) === getTaskPrimaryInventoryItemId(task),
canRelease: viewerContext.canManageSensitiveInventory && canReleaseTaskInventoryBinding(task, binding),
}
}
export function buildOrderBindingSummary(
tasks: TaskRow[] | null | undefined,
taskBindingSummaryMap: Map<number, AdminTaskBindingSummary> = new Map(),
): OrderBindingSummary {
const normalizedTasks = Array.isArray(tasks) ? tasks : []
const totalTaskCount = normalizedTasks.length
const taskBindings = normalizedTasks.map((task) => buildTaskBindingState(task))
const systemBoundTaskCount = normalizedTasks.filter((task) => isTaskSystemBound(task)).length
const completedBindingTaskCount = normalizedTasks.filter((task) => String(task?.task_status || '') === 'redeemed').length
const bindingSummaries = normalizedTasks.map((task) => getTaskBindingSummary(taskBindingSummaryMap, task.id))
const totalBindingCount = bindingSummaries.reduce((sum, item) => sum + item.totalBindingCount, 0)
const reservedBindingCount = bindingSummaries.reduce((sum, item) => sum + item.reservedBindingCount, 0)
const consumedBindingCount = bindingSummaries.reduce((sum, item) => sum + item.consumedBindingCount, 0)
const releasedBindingCount = bindingSummaries.reduce((sum, item) => sum + item.releasedBindingCount, 0)
const totalBindingCount = 0
const reservedBindingCount = 0
const consumedBindingCount = 0
const releasedBindingCount = 0
let systemBindingStatus = 'pending_binding'
let userBindingStatus = 'not_started'
@@ -280,7 +193,7 @@ export function buildOrderBindingSummary(
userBindingStatus = 'binding_exception'
} else if (systemBoundTaskCount > 0) {
systemBindingStatus = 'system_bound'
} else if (taskBindings.some((item) => ['manual_review', 'retry_pending', 'waiting_inventory'].includes(item.systemBindingStatus))) {
} else if (taskBindings.some((item) => ['manual_review', 'retry_pending'].includes(item.systemBindingStatus))) {
systemBindingStatus = 'binding_exception'
}
@@ -297,18 +210,6 @@ export function buildOrderBindingSummary(
}
}
function canReleaseTaskInventoryBinding(task: TaskRow | null | undefined, binding: TaskInventoryBindingLike | null | undefined): boolean {
if (!task || !binding) {
return false
}
if (String(binding.binding_status || '').trim() !== 'reserved') {
return false
}
return !['redeemed', 'expired'].includes(String(task.task_status || '').trim())
}
function buildTaskBindingState(task: TaskRow | null | undefined): TaskBindingState {
const normalizedStatus = String(task?.task_status || '').trim()
@@ -327,10 +228,6 @@ function buildTaskBindingState(task: TaskRow | null | undefined): TaskBindingSta
return { systemBindingStatus: 'pending_binding', userBindingStatus: 'not_started' }
}
if (normalizedStatus === 'waiting_inventory') {
return { systemBindingStatus: 'waiting_inventory', userBindingStatus: 'not_started' }
}
if (normalizedStatus === 'pending_binding_prepare') {
return { systemBindingStatus: 'pending_binding', userBindingStatus: 'not_started' }
}
@@ -391,24 +288,13 @@ function buildTaskBindingState(task: TaskRow | null | undefined): TaskBindingSta
}
function isTaskSystemBound(task: TaskRow | null | undefined): boolean {
return Boolean(task && (getTaskPrimaryInventoryItemId(task) || getTaskPrimaryClaimTokenId(task)))
return Boolean(task && getTaskPrimaryClaimTokenId(task))
}
function normalizeRecord(value: unknown): JsonRecord {
return value && typeof value === 'object' && !Array.isArray(value) ? value : {}
}
function maskCode(value: unknown): string {
const text = String(value || '').trim()
if (!text) {
return ''
}
if (text.length <= 8) {
return `${text.slice(0, 2)}****${text.slice(-2)}`
}
return `${text.slice(0, 4)}****${text.slice(-4)}`
}
function getTaskRetryCount(task: TaskRow | null | undefined): number {
return Number(task?.attempt_count || 0)
}