From 21a51157d1cf10bd71a9964414a965cbed41efb2 Mon Sep 17 00:00:00 2001 From: yml Date: Thu, 21 May 2026 16:58:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8E=E7=AB=AF=E8=BF=81=E7=A7=BB=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=E7=AE=A1=E7=90=86=E4=BB=BB=E5=8A=A1=E8=AF=BB=E8=BE=85?= =?UTF-8?q?=E5=8A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...-helpers.js => admin-task-read-helpers.ts} | 145 ++++++++++++------ docs/backend-typescript-migration-plan.md | 8 + 2 files changed, 110 insertions(+), 43 deletions(-) rename apps/backend/src/services/admin/{admin-task-read-helpers.js => admin-task-read-helpers.ts} (77%) diff --git a/apps/backend/src/services/admin/admin-task-read-helpers.js b/apps/backend/src/services/admin/admin-task-read-helpers.ts similarity index 77% rename from apps/backend/src/services/admin/admin-task-read-helpers.js rename to apps/backend/src/services/admin/admin-task-read-helpers.ts index 94396f2f..ae9092db 100644 --- a/apps/backend/src/services/admin/admin-task-read-helpers.js +++ b/apps/backend/src/services/admin/admin-task-read-helpers.ts @@ -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 + +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 { 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 = []): Promise> { const rows = await listTaskInventoryBindingSummariesByTaskIds(taskIds) - const output = new Map() + const output = new Map() - 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, + taskId: number | string, +): AdminTaskBindingSummary { return summaryMap.get(Number(taskId)) || createEmptyTaskBindingSummary() } -export function createTaskBindingSummaryFromBindings(bindings = []) { +export function createTaskBindingSummaryFromBindings( + bindings: Array> | 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 = 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 | 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((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) } diff --git a/docs/backend-typescript-migration-plan.md b/docs/backend-typescript-migration-plan.md index 260e9cd7..bbbc4c14 100644 --- a/docs/backend-typescript-migration-plan.md +++ b/docs/backend-typescript-migration-plan.md @@ -752,6 +752,14 @@ - `npm run typecheck` - `npm run build` - `npm test` 共 139 个用例通过 +189. 后台 Admin 任务读侧映射 helper 迁移到 `.ts`: + - `src/services/admin/admin-task-read-helpers.ts` +190. Admin 任务摘要 / 列表项 / 操作 payload、任务事件、任务库存绑定、任务绑定汇总 map、订单绑定状态汇总与 Agiso 自动发货订单摘要已进入 TS 编译链路;任务 row、绑定 row、事件 row、viewer context、绑定 summary、自动发货 summary 与动态 metadata 补齐类型 +191. Docker 内验证通过: + - `src/services/admin/*.test.js` 共 8 个用例通过 + - `npm run typecheck` + - `npm run build` + - `npm test` 共 139 个用例通过 ## 下一步建议