From 5753971c92970ee473b622fffe03ac0d188ee273 Mon Sep 17 00:00:00 2001 From: yml Date: Thu, 21 May 2026 14:01:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8E=E7=AB=AF=E8=BF=81=E7=A7=BB=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E4=BB=93=E5=82=A8=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{task-repo.js => task-repo.ts} | 101 ++++++++++-------- apps/backend/src/types/repository-rows.ts | 9 ++ docs/backend-typescript-migration-plan.md | 9 +- 3 files changed, 73 insertions(+), 46 deletions(-) rename apps/backend/src/repositories/{task-repo.js => task-repo.ts} (81%) diff --git a/apps/backend/src/repositories/task-repo.js b/apps/backend/src/repositories/task-repo.ts similarity index 81% rename from apps/backend/src/repositories/task-repo.js rename to apps/backend/src/repositories/task-repo.ts index 835cc355..687d2e18 100644 --- a/apps/backend/src/repositories/task-repo.js +++ b/apps/backend/src/repositories/task-repo.ts @@ -1,13 +1,31 @@ -// @ts-check - import { query, withTransaction } from '../db/client.js' +import type { PoolClient } from 'pg' -/** @typedef {import('../types/repository-inputs.js').TaskCreateInput} TaskCreateInput */ -/** @typedef {import('../types/repository-inputs.js').TaskListQueryInput} TaskListQueryInput */ -/** @typedef {import('../types/repository-inputs.js').TaskTencentContextPatch} TaskTencentContextPatch */ -/** @typedef {import('../types/repository-inputs.js').TaskUpdatePatch} TaskUpdatePatch */ -/** @typedef {import('../types/repository-rows.js').TaskListQueryResult} TaskListQueryResult */ -/** @typedef {import('../types/repository-rows.js').TaskRow} TaskRow */ +import type { + TaskCreateInput, + TaskListQueryInput, + TaskTencentContextPatch, + TaskUpdatePatch, +} from '../types/repository-inputs.js' +import type { + TaskListQueryResult, + TaskRow, +} from '../types/repository-rows.js' + +type TaskQueryExecutor = (text: string, params?: unknown[]) => Promise<{ rows: unknown[] }> + +type TencentBrowserContextRow = { + browser_session_id: string + login_type: string + nickname: string + role_id: string + role_name: string + area: string + partition_name: string + screenshot_path: string + artifacts_json: string | Record + state_json: string | Record +} const TASK_FIELDS = ` ft.*, @@ -55,28 +73,25 @@ const TASK_JOINS = ` ) inv ON TRUE ` -function buildTaskSelect(extraFields = '') { +function buildTaskSelect(extraFields = ''): string { const normalizedExtra = String(extraFields || '').trim() const fieldSql = normalizedExtra ? `${TASK_FIELDS}, ${normalizedExtra}` : TASK_FIELDS return `SELECT ${fieldSql} ${TASK_JOINS}` } -/** @returns {Promise} */ -export async function listTasksByOrderId(orderId) { - const result = await query( +export async function listTasksByOrderId(orderId: number | string): Promise { + const result = await query( `${buildTaskSelect()} WHERE ft.order_id = $1 ORDER BY ft.id ASC`, [Number(orderId)], ) - return /** @type {TaskRow[]} */ (result.rows) + return result.rows } -/** @returns {Promise} */ -/** @param {TaskCreateInput} input */ -export async function createTask(input) { +export async function createTask(input: TaskCreateInput): Promise { return withTransaction(async (client) => { - const taskResult = await client.query( + const taskResult = await client.query<{ id: number }>( ` INSERT INTO fulfillment_tasks ( order_id, @@ -147,13 +162,11 @@ export async function createTask(input) { await upsertTencentBrowserContextWithClient(client, taskId, input.tencentContext, input.createdAt) } - return getTaskByIdWithExecutor(client.query.bind(client), taskId) + return getTaskByIdWithExecutor(client.query.bind(client) as TaskQueryExecutor, taskId) }) } -/** @returns {Promise} */ -/** @param {TaskUpdatePatch} patch */ -export async function updateTask(taskId, patch) { +export async function updateTask(taskId: number | string, patch: TaskUpdatePatch): Promise { const current = await getTaskById(taskId) if (!current) { return null @@ -222,29 +235,25 @@ export async function updateTask(taskId, patch) { }, patch.updated_at || current.updated_at) } - return getTaskByIdWithExecutor(client.query.bind(client), taskId) + return getTaskByIdWithExecutor(client.query.bind(client) as TaskQueryExecutor, taskId) }) } -/** @returns {Promise} */ -export async function getTaskById(taskId) { +export async function getTaskById(taskId: number | string): Promise { return getTaskByIdWithExecutor(query, taskId) } -/** @returns {Promise} */ -export async function findTaskByClaimTokenId(claimTokenId) { - const result = await query( +export async function findTaskByClaimTokenId(claimTokenId: number | string): Promise { + const result = await query( `${buildTaskSelect()} JOIN claim_tokens ctf ON ctf.task_id = ft.id WHERE ctf.id = $1 LIMIT 1`, [Number(claimTokenId)], ) - return /** @type {TaskRow | null} */ (result.rows[0] || null) + return result.rows[0] || null } -/** @returns {Promise} */ -/** @param {TaskListQueryInput} [queryInput] */ export async function listTasks({ page = 1, pageSize = 20, @@ -255,10 +264,10 @@ export async function listTasks({ roleId = '', dateFrom = '', dateTo = '', -} = /** @type {TaskListQueryInput} */ ({})) { +}: TaskListQueryInput = {}): Promise { const offset = (page - 1) * pageSize - const filters = [] - const params = [] + const filters: string[] = [] + const params: unknown[] = [] if (status) { params.push(status) @@ -296,7 +305,7 @@ export async function listTasks({ } const whereClause = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '' - const totalResult = await query( + const totalResult = await query<{ [column: string]: unknown, total: number }>( ` SELECT COUNT(*)::int AS total FROM fulfillment_tasks ft @@ -309,7 +318,7 @@ export async function listTasks({ params.push(pageSize) params.push(offset) - const itemsResult = await query( + const itemsResult = await query( `${buildTaskSelect('oi.sku_code, oi.sku_name, oi.quantity')} LEFT JOIN order_items oi ON oi.id = ft.order_item_id ${whereClause} @@ -319,14 +328,18 @@ export async function listTasks({ ) return { - items: /** @type {TaskRow[]} */ (itemsResult.rows), + items: itemsResult.rows, total: Number(totalResult.rows[0]?.total || 0), } } -/** @param {TaskTencentContextPatch} patch */ -async function upsertTencentBrowserContextWithClient(client, taskId, patch = {}, timestamp) { - const currentResult = await client.query( +async function upsertTencentBrowserContextWithClient( + client: PoolClient, + taskId: number | string, + patch: TaskTencentContextPatch = {}, + timestamp: string | undefined, +): Promise { + const currentResult = await client.query( 'SELECT * FROM tencent_browser_contexts WHERE task_id = $1 LIMIT 1', [Number(taskId)], ) @@ -418,18 +431,20 @@ async function upsertTencentBrowserContextWithClient(client, taskId, patch = {}, ) } -/** @returns {Promise} */ -async function getTaskByIdWithExecutor(executor, taskId) { +async function getTaskByIdWithExecutor( + executor: TaskQueryExecutor, + taskId: number | string, +): Promise { const result = await executor( `${buildTaskSelect()} WHERE ft.id = $1 LIMIT 1`, [Number(taskId)], ) - return /** @type {TaskRow | null} */ (result.rows[0] || null) + return (result.rows[0] as TaskRow | undefined) || null } -function containsTencentPatch(patch = {}) { +function containsTencentPatch(patch: TaskUpdatePatch = {}): boolean { return [ 'browser_session_id', 'login_type', diff --git a/apps/backend/src/types/repository-rows.ts b/apps/backend/src/types/repository-rows.ts index fbbae5e2..9b8ab5ea 100644 --- a/apps/backend/src/types/repository-rows.ts +++ b/apps/backend/src/types/repository-rows.ts @@ -39,6 +39,7 @@ export type TaskRow = { order_id: number order_item_id: number platform_order_id: string + profile_id: number task_no: string executor_key: string task_status: string @@ -46,17 +47,25 @@ export type TaskRow = { result_code: string result_message: string inventory_status: string + automation_mode: string + requires_claim: boolean user_action_status: string + attempt_count: number browser_session_id: string login_type: string + nickname: string role_name: string role_id: string + area: string + partition_name: string claim_token: string claim_expires_at?: string | null primary_claim_token: string primary_claim_token_id: number | null + primary_claim_token_status: string primary_claim_expires_at?: string | null primary_inventory_item_id: number | null + primary_inventory_binding_status: string inventory_display_value: string primary_inventory_display_value: string inventory_credential_type?: string diff --git a/docs/backend-typescript-migration-plan.md b/docs/backend-typescript-migration-plan.md index f56b13aa..aa196167 100644 --- a/docs/backend-typescript-migration-plan.md +++ b/docs/backend-typescript-migration-plan.md @@ -233,14 +233,17 @@ - `src/repositories/task-inventory-binding-repo.ts` 16. 核心库存 repository 已迁移到 `.ts`: - `src/repositories/inventory-repo.ts` +17. 核心任务 repository 已迁移到 `.ts`: + - `src/repositories/task-repo.ts` +18. `TaskRow` 已补齐任务读取链路实际使用的主表、领取 token、腾讯上下文、库存绑定字段 +19. 核心 repository 迁移阶段已收口,Docker 内 `typecheck / build / test` 继续通过 ## 下一步建议 第一批继续推进时,建议按这个顺序: -1. 继续迁移剩余核心 repository:`task-repo` -2. 拆分并迁移 `runtime.js`,把 env 解析、默认配置加载、配置合并分开 -3. 为 webhook、库存换码、自动发货补测试 +1. 拆分并迁移 `runtime.js`,把 env 解析、默认配置加载、配置合并分开 +2. 为 webhook、库存换码、自动发货补测试 ## 执行原则