后端迁移后台管理任务读辅助

This commit is contained in:
yml
2026-05-21 16:58:16 +08:00
parent 41a2360adf
commit 21a51157d1
2 changed files with 110 additions and 43 deletions
@@ -1,5 +1,3 @@
// @ts-check
import { getTaskById } from '../../repositories/task-repo.js'
import { listTaskInventoryBindingSummariesByTaskIds } from '../../repositories/task-inventory-binding-repo.js'
import { createHttpError } from '../../utils/http.js'
@@ -11,13 +9,60 @@ import {
parseTaskContext,
} from './admin-read-shared-helpers.js'
/** @typedef {import('../../types/admin-read-models.js').AdminTaskBindingSummary} AdminTaskBindingSummary */
/** @typedef {import('../../types/admin-read-models.js').AdminTaskListItem} AdminTaskListItem */
/** @typedef {import('../../types/repository-rows.js').TaskEventRow} TaskEventRow */
/** @typedef {import('../../types/repository-rows.js').TaskInventoryBindingRow} TaskInventoryBindingRow */
/** @typedef {import('../../types/repository-rows.js').TaskRow} TaskRow */
import type {
AdminAgisoAutoDeliveryStatus,
AdminTaskBindingSummary,
AdminTaskListItem,
} from '../../types/admin-read-models.js'
import type { AdminTaskActionPayload } from '../../types/admin-write-models.js'
import type {
OrderRow,
TaskEventRow,
TaskInventoryBindingRow,
TaskInventoryBindingSummaryRow,
TaskRow,
} from '../../types/repository-rows.js'
import type { AdminViewerContext } from './admin-read-shared-helpers.js'
export function mapAdminTaskSummary(task, bindingSummary = createEmptyTaskBindingSummary()) {
type JsonRecord = Record<string, any>
type TaskBindingState = {
systemBindingStatus: string
userBindingStatus: string
}
type TaskInventoryBindingLike = TaskInventoryBindingRow & {
inventory_item_status?: string
}
type OrderBindingSummary = {
totalTaskCount: number
systemBoundTaskCount: number
completedBindingTaskCount: number
totalBindingCount: number
reservedBindingCount: number
consumedBindingCount: number
releasedBindingCount: number
systemBindingStatus: string
userBindingStatus: string
}
type OrderAgisoAutoDeliverySummary = AdminAgisoAutoDeliveryStatus & {
sourceTaskId: number | null
sourceTaskNo: string
totalTaskCount: number
deliveredTaskCount: number
}
type OrderAgisoAutoDeliveryCandidate = AdminAgisoAutoDeliveryStatus & {
sourceTaskId: number | null
sourceTaskNo: string
}
export function mapAdminTaskSummary(
task: TaskRow,
bindingSummary: AdminTaskBindingSummary = createEmptyTaskBindingSummary(),
): JsonRecord {
const binding = buildTaskBindingState(task)
const taskContext = parseTaskContext(task)
@@ -42,7 +87,7 @@ export function mapAdminTaskSummary(task, bindingSummary = createEmptyTaskBindin
}
}
export function mapTaskActionPayload(task) {
export function mapTaskActionPayload(task: any): AdminTaskActionPayload {
const inventoryItemId = getTaskPrimaryInventoryItemId(task)
const primaryClaimTokenId = getTaskPrimaryClaimTokenId(task)
@@ -60,7 +105,7 @@ export function mapTaskActionPayload(task) {
}
}
export async function getRequiredTask(taskId) {
export async function getRequiredTask(taskId: number | string): Promise<TaskRow> {
const task = await getTaskById(Number(taskId))
if (!task) {
@@ -73,11 +118,11 @@ export async function getRequiredTask(taskId) {
return task
}
/**
* @param {TaskRow} task
* @returns {AdminTaskListItem}
*/
export function mapAdminTaskListItem(task, bindingSummary = createEmptyTaskBindingSummary(), viewerContext = createAdminViewerContext()) {
export function mapAdminTaskListItem(
task: TaskRow,
bindingSummary: AdminTaskBindingSummary = createEmptyTaskBindingSummary(),
viewerContext: AdminViewerContext = createAdminViewerContext(),
): AdminTaskListItem {
const binding = buildTaskBindingState(task)
const taskContext = parseTaskContext(task)
@@ -114,8 +159,7 @@ export function mapAdminTaskListItem(task, bindingSummary = createEmptyTaskBindi
}
}
/** @param {TaskEventRow} event */
export function mapAdminTaskEvent(event) {
export function mapAdminTaskEvent(event: TaskEventRow): JsonRecord {
const payload = safeParseJson(event.payload_json)
return {
@@ -126,8 +170,7 @@ export function mapAdminTaskEvent(event) {
}
}
/** @returns {AdminTaskBindingSummary} */
export function createEmptyTaskBindingSummary() {
export function createEmptyTaskBindingSummary(): AdminTaskBindingSummary {
return {
totalBindingCount: 0,
reservedBindingCount: 0,
@@ -137,11 +180,11 @@ export function createEmptyTaskBindingSummary() {
}
}
export async function getTaskBindingSummaryMap(taskIds = []) {
export async function getTaskBindingSummaryMap(taskIds: Array<number | string> = []): Promise<Map<number, AdminTaskBindingSummary>> {
const rows = await listTaskInventoryBindingSummariesByTaskIds(taskIds)
const output = new Map()
const output = new Map<number, AdminTaskBindingSummary>()
for (const row of rows) {
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),
@@ -156,11 +199,16 @@ export async function getTaskBindingSummaryMap(taskIds = []) {
return output
}
export function getTaskBindingSummary(summaryMap, taskId) {
export function getTaskBindingSummary(
summaryMap: Map<number, AdminTaskBindingSummary>,
taskId: number | string,
): AdminTaskBindingSummary {
return summaryMap.get(Number(taskId)) || createEmptyTaskBindingSummary()
}
export function createTaskBindingSummaryFromBindings(bindings = []) {
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())
@@ -175,7 +223,11 @@ export function createTaskBindingSummaryFromBindings(bindings = []) {
}
}
export function mapAdminTaskInventoryBinding(binding, task, viewerContext = createAdminViewerContext()) {
export function mapAdminTaskInventoryBinding(
binding: TaskInventoryBindingLike,
task: TaskRow,
viewerContext: AdminViewerContext = createAdminViewerContext(),
): JsonRecord {
const metadata = safeParseJson(binding.metadata_json)
return {
@@ -200,7 +252,10 @@ export function mapAdminTaskInventoryBinding(binding, task, viewerContext = crea
}
}
export function buildOrderBindingSummary(tasks, taskBindingSummaryMap = new Map()) {
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))
@@ -260,7 +315,10 @@ export function buildOrderBindingSummary(tasks, taskBindingSummaryMap = new Map(
}
}
export function buildOrderAgisoAutoDeliverySummary(order, tasks = []) {
export function buildOrderAgisoAutoDeliverySummary(
order: Partial<OrderRow> | null | undefined,
tasks: TaskRow[] = [],
): OrderAgisoAutoDeliverySummary | null {
if (String(order?.provider || '').trim() !== 'agiso' || String(order?.platform || '').trim() !== 'xianyu') {
return null
}
@@ -268,7 +326,7 @@ export function buildOrderAgisoAutoDeliverySummary(order, tasks = []) {
const normalizedTasks = Array.isArray(tasks) ? tasks.filter(Boolean) : []
const totalTaskCount = normalizedTasks.length
const deliveredTaskCount = normalizedTasks.filter((task) => String(task?.delivery_status || '').trim() === 'delivered').length
const latest = normalizedTasks.reduce((best, task) => {
const latest = normalizedTasks.reduce<OrderAgisoAutoDeliveryCandidate | null>((best, task) => {
const autoDelivery = mapAgisoAutoDeliveryContext(parseTaskContext(task).agisoAutoDelivery)
if (!autoDelivery) {
@@ -314,7 +372,7 @@ export function buildOrderAgisoAutoDeliverySummary(order, tasks = []) {
}
}
function canReleaseTaskInventoryBinding(task, binding) {
function canReleaseTaskInventoryBinding(task: TaskRow | null | undefined, binding: TaskInventoryBindingLike | null | undefined): boolean {
if (!task || !binding) {
return false
}
@@ -326,7 +384,7 @@ function canReleaseTaskInventoryBinding(task, binding) {
return !['redeemed', 'expired'].includes(String(task.task_status || '').trim())
}
function buildTaskBindingState(task) {
function buildTaskBindingState(task: TaskRow | null | undefined): TaskBindingState {
const normalizedStatus = String(task?.task_status || '').trim()
if (!normalizedStatus) {
@@ -407,32 +465,33 @@ function buildTaskBindingState(task) {
return { systemBindingStatus: 'pending_binding', userBindingStatus: 'not_started' }
}
function isTaskSystemBound(task) {
function isTaskSystemBound(task: TaskRow | null | undefined): boolean {
return Boolean(task && (getTaskPrimaryInventoryItemId(task) || getTaskPrimaryClaimTokenId(task)))
}
function mapAgisoAutoDeliveryContext(value) {
function mapAgisoAutoDeliveryContext(value: unknown): AdminAgisoAutoDeliveryStatus | null {
if (!value || typeof value !== 'object') {
return null
}
const record = value as JsonRecord
return {
status: String(value.status || '').trim(),
trigger: String(value.trigger || '').trim(),
reason: String(value.reason || '').trim(),
platformOrderId: String(value.platformOrderId || '').trim(),
responseStatus: Number(value.responseStatus || 0),
errorMessage: String(value.errorMessage || '').trim(),
requestId: String(value.requestId || '').trim(),
updatedAt: value.updatedAt || null,
status: String(record.status || '').trim(),
trigger: String(record.trigger || '').trim(),
reason: String(record.reason || '').trim(),
platformOrderId: String(record.platformOrderId || '').trim(),
responseStatus: Number(record.responseStatus || 0),
errorMessage: String(record.errorMessage || '').trim(),
requestId: String(record.requestId || '').trim(),
updatedAt: record.updatedAt || null,
}
}
function normalizeRecord(value) {
function normalizeRecord(value: unknown): JsonRecord {
return value && typeof value === 'object' && !Array.isArray(value) ? value : {}
}
function maskCode(value) {
function maskCode(value: unknown): string {
const text = String(value || '').trim()
if (!text) {
return ''
@@ -443,6 +502,6 @@ function maskCode(value) {
return `${text.slice(0, 4)}****${text.slice(-4)}`
}
function getTaskRetryCount(task) {
function getTaskRetryCount(task: TaskRow | null | undefined): number {
return Number(task?.attempt_count || 0)
}