后端迁移任务仓储模块

This commit is contained in:
yml
2026-05-21 14:01:54 +08:00
parent b803bbd007
commit 5753971c92
3 changed files with 73 additions and 46 deletions
@@ -1,13 +1,31 @@
// @ts-check
import { query, withTransaction } from '../db/client.js' import { query, withTransaction } from '../db/client.js'
import type { PoolClient } from 'pg'
/** @typedef {import('../types/repository-inputs.js').TaskCreateInput} TaskCreateInput */ import type {
/** @typedef {import('../types/repository-inputs.js').TaskListQueryInput} TaskListQueryInput */ TaskCreateInput,
/** @typedef {import('../types/repository-inputs.js').TaskTencentContextPatch} TaskTencentContextPatch */ TaskListQueryInput,
/** @typedef {import('../types/repository-inputs.js').TaskUpdatePatch} TaskUpdatePatch */ TaskTencentContextPatch,
/** @typedef {import('../types/repository-rows.js').TaskListQueryResult} TaskListQueryResult */ TaskUpdatePatch,
/** @typedef {import('../types/repository-rows.js').TaskRow} TaskRow */ } 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<string, unknown>
state_json: string | Record<string, unknown>
}
const TASK_FIELDS = ` const TASK_FIELDS = `
ft.*, ft.*,
@@ -55,28 +73,25 @@ const TASK_JOINS = `
) inv ON TRUE ) inv ON TRUE
` `
function buildTaskSelect(extraFields = '') { function buildTaskSelect(extraFields = ''): string {
const normalizedExtra = String(extraFields || '').trim() const normalizedExtra = String(extraFields || '').trim()
const fieldSql = normalizedExtra ? `${TASK_FIELDS}, ${normalizedExtra}` : TASK_FIELDS const fieldSql = normalizedExtra ? `${TASK_FIELDS}, ${normalizedExtra}` : TASK_FIELDS
return `SELECT ${fieldSql} ${TASK_JOINS}` return `SELECT ${fieldSql} ${TASK_JOINS}`
} }
/** @returns {Promise<TaskRow[]>} */ export async function listTasksByOrderId(orderId: number | string): Promise<TaskRow[]> {
export async function listTasksByOrderId(orderId) { const result = await query<TaskRow>(
const result = await query(
`${buildTaskSelect()} `${buildTaskSelect()}
WHERE ft.order_id = $1 WHERE ft.order_id = $1
ORDER BY ft.id ASC`, ORDER BY ft.id ASC`,
[Number(orderId)], [Number(orderId)],
) )
return /** @type {TaskRow[]} */ (result.rows) return result.rows
} }
/** @returns {Promise<TaskRow | null>} */ export async function createTask(input: TaskCreateInput): Promise<TaskRow | null> {
/** @param {TaskCreateInput} input */
export async function createTask(input) {
return withTransaction(async (client) => { return withTransaction(async (client) => {
const taskResult = await client.query( const taskResult = await client.query<{ id: number }>(
` `
INSERT INTO fulfillment_tasks ( INSERT INTO fulfillment_tasks (
order_id, order_id,
@@ -147,13 +162,11 @@ export async function createTask(input) {
await upsertTencentBrowserContextWithClient(client, taskId, input.tencentContext, input.createdAt) 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<TaskRow | null>} */ export async function updateTask(taskId: number | string, patch: TaskUpdatePatch): Promise<TaskRow | null> {
/** @param {TaskUpdatePatch} patch */
export async function updateTask(taskId, patch) {
const current = await getTaskById(taskId) const current = await getTaskById(taskId)
if (!current) { if (!current) {
return null return null
@@ -222,29 +235,25 @@ export async function updateTask(taskId, patch) {
}, patch.updated_at || current.updated_at) }, patch.updated_at || current.updated_at)
} }
return getTaskByIdWithExecutor(client.query.bind(client), taskId) return getTaskByIdWithExecutor(client.query.bind(client) as TaskQueryExecutor, taskId)
}) })
} }
/** @returns {Promise<TaskRow | null>} */ export async function getTaskById(taskId: number | string): Promise<TaskRow | null> {
export async function getTaskById(taskId) {
return getTaskByIdWithExecutor(query, taskId) return getTaskByIdWithExecutor(query, taskId)
} }
/** @returns {Promise<TaskRow | null>} */ export async function findTaskByClaimTokenId(claimTokenId: number | string): Promise<TaskRow | null> {
export async function findTaskByClaimTokenId(claimTokenId) { const result = await query<TaskRow>(
const result = await query(
`${buildTaskSelect()} `${buildTaskSelect()}
JOIN claim_tokens ctf ON ctf.task_id = ft.id JOIN claim_tokens ctf ON ctf.task_id = ft.id
WHERE ctf.id = $1 WHERE ctf.id = $1
LIMIT 1`, LIMIT 1`,
[Number(claimTokenId)], [Number(claimTokenId)],
) )
return /** @type {TaskRow | null} */ (result.rows[0] || null) return result.rows[0] || null
} }
/** @returns {Promise<TaskListQueryResult>} */
/** @param {TaskListQueryInput} [queryInput] */
export async function listTasks({ export async function listTasks({
page = 1, page = 1,
pageSize = 20, pageSize = 20,
@@ -255,10 +264,10 @@ export async function listTasks({
roleId = '', roleId = '',
dateFrom = '', dateFrom = '',
dateTo = '', dateTo = '',
} = /** @type {TaskListQueryInput} */ ({})) { }: TaskListQueryInput = {}): Promise<TaskListQueryResult> {
const offset = (page - 1) * pageSize const offset = (page - 1) * pageSize
const filters = [] const filters: string[] = []
const params = [] const params: unknown[] = []
if (status) { if (status) {
params.push(status) params.push(status)
@@ -296,7 +305,7 @@ export async function listTasks({
} }
const whereClause = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '' 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 SELECT COUNT(*)::int AS total
FROM fulfillment_tasks ft FROM fulfillment_tasks ft
@@ -309,7 +318,7 @@ export async function listTasks({
params.push(pageSize) params.push(pageSize)
params.push(offset) params.push(offset)
const itemsResult = await query( const itemsResult = await query<TaskRow>(
`${buildTaskSelect('oi.sku_code, oi.sku_name, oi.quantity')} `${buildTaskSelect('oi.sku_code, oi.sku_name, oi.quantity')}
LEFT JOIN order_items oi ON oi.id = ft.order_item_id LEFT JOIN order_items oi ON oi.id = ft.order_item_id
${whereClause} ${whereClause}
@@ -319,14 +328,18 @@ export async function listTasks({
) )
return { return {
items: /** @type {TaskRow[]} */ (itemsResult.rows), items: itemsResult.rows,
total: Number(totalResult.rows[0]?.total || 0), total: Number(totalResult.rows[0]?.total || 0),
} }
} }
/** @param {TaskTencentContextPatch} patch */ async function upsertTencentBrowserContextWithClient(
async function upsertTencentBrowserContextWithClient(client, taskId, patch = {}, timestamp) { client: PoolClient,
const currentResult = await client.query( taskId: number | string,
patch: TaskTencentContextPatch = {},
timestamp: string | undefined,
): Promise<void> {
const currentResult = await client.query<TencentBrowserContextRow>(
'SELECT * FROM tencent_browser_contexts WHERE task_id = $1 LIMIT 1', 'SELECT * FROM tencent_browser_contexts WHERE task_id = $1 LIMIT 1',
[Number(taskId)], [Number(taskId)],
) )
@@ -418,18 +431,20 @@ async function upsertTencentBrowserContextWithClient(client, taskId, patch = {},
) )
} }
/** @returns {Promise<TaskRow | null>} */ async function getTaskByIdWithExecutor(
async function getTaskByIdWithExecutor(executor, taskId) { executor: TaskQueryExecutor,
taskId: number | string,
): Promise<TaskRow | null> {
const result = await executor( const result = await executor(
`${buildTaskSelect()} `${buildTaskSelect()}
WHERE ft.id = $1 WHERE ft.id = $1
LIMIT 1`, LIMIT 1`,
[Number(taskId)], [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 [ return [
'browser_session_id', 'browser_session_id',
'login_type', 'login_type',
@@ -39,6 +39,7 @@ export type TaskRow = {
order_id: number order_id: number
order_item_id: number order_item_id: number
platform_order_id: string platform_order_id: string
profile_id: number
task_no: string task_no: string
executor_key: string executor_key: string
task_status: string task_status: string
@@ -46,17 +47,25 @@ export type TaskRow = {
result_code: string result_code: string
result_message: string result_message: string
inventory_status: string inventory_status: string
automation_mode: string
requires_claim: boolean
user_action_status: string user_action_status: string
attempt_count: number
browser_session_id: string browser_session_id: string
login_type: string login_type: string
nickname: string
role_name: string role_name: string
role_id: string role_id: string
area: string
partition_name: string
claim_token: string claim_token: string
claim_expires_at?: string | null claim_expires_at?: string | null
primary_claim_token: string primary_claim_token: string
primary_claim_token_id: number | null primary_claim_token_id: number | null
primary_claim_token_status: string
primary_claim_expires_at?: string | null primary_claim_expires_at?: string | null
primary_inventory_item_id: number | null primary_inventory_item_id: number | null
primary_inventory_binding_status: string
inventory_display_value: string inventory_display_value: string
primary_inventory_display_value: string primary_inventory_display_value: string
inventory_credential_type?: string inventory_credential_type?: string
+6 -3
View File
@@ -233,14 +233,17 @@
- `src/repositories/task-inventory-binding-repo.ts` - `src/repositories/task-inventory-binding-repo.ts`
16. 核心库存 repository 已迁移到 `.ts` 16. 核心库存 repository 已迁移到 `.ts`
- `src/repositories/inventory-repo.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` 1. 拆分并迁移 `runtime.js`,把 env 解析、默认配置加载、配置合并分开
2. 拆分并迁移 `runtime.js`,把 env 解析、默认配置加载、配置合并分开 2. 为 webhook、库存换码、自动发货补测试
3. 为 webhook、库存换码、自动发货补测试
## 执行原则 ## 执行原则