彻底重构-4
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
import { query, withTransaction } from '../db/client.js'
|
||||
|
||||
const INVENTORY_ITEM_SELECT = `
|
||||
SELECT
|
||||
ii.id,
|
||||
ii.sku_code,
|
||||
ii.batch_no,
|
||||
ii.credential_type,
|
||||
ii.display_value,
|
||||
ii.status,
|
||||
ii.invalid_reason,
|
||||
ii.consumed_at AS delivered_at,
|
||||
ii.created_at,
|
||||
ii.updated_at,
|
||||
tib.task_id AS reserved_by_task_id,
|
||||
ft.task_no AS reserved_by_task_no,
|
||||
ft.platform_order_id
|
||||
FROM inventory_items ii
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT *
|
||||
FROM task_inventory_bindings
|
||||
WHERE inventory_item_id = ii.id AND binding_status IN ('reserved', 'consumed')
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
) tib ON TRUE
|
||||
LEFT JOIN fulfillment_tasks ft ON ft.id = tib.task_id
|
||||
`
|
||||
|
||||
export async function findFirstAvailableInventoryItemBySkuCode(skuCode, credentialType = 'tencent_code') {
|
||||
const result = await query(
|
||||
`${INVENTORY_ITEM_SELECT}
|
||||
WHERE ii.sku_code = $1 AND ii.credential_type = $2 AND ii.status = 'available'
|
||||
ORDER BY ii.id ASC
|
||||
LIMIT 1`,
|
||||
[skuCode, credentialType],
|
||||
)
|
||||
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export async function assignReservedInventoryItem(inventoryItemId, taskId, updatedAt, roleKey = 'primary_code') {
|
||||
return withTransaction(async (client) => {
|
||||
const inventoryResult = await client.query(
|
||||
`
|
||||
UPDATE inventory_items
|
||||
SET status = 'reserved', updated_at = $1
|
||||
WHERE id = $2 AND status = 'available'
|
||||
RETURNING id
|
||||
`,
|
||||
[updatedAt, Number(inventoryItemId)],
|
||||
)
|
||||
|
||||
if (!inventoryResult.rows[0]) {
|
||||
return getInventoryItemById(inventoryItemId)
|
||||
}
|
||||
|
||||
await client.query(
|
||||
`
|
||||
INSERT INTO task_inventory_bindings (
|
||||
task_id,
|
||||
inventory_item_id,
|
||||
role_key,
|
||||
quantity,
|
||||
binding_status,
|
||||
metadata_json,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES ($1, $2, $3, 1, 'reserved', '{}'::jsonb, $4, $4)
|
||||
ON CONFLICT (task_id, role_key, inventory_item_id) DO UPDATE
|
||||
SET binding_status = 'reserved', updated_at = EXCLUDED.updated_at, released_at = NULL
|
||||
`,
|
||||
[Number(taskId), Number(inventoryItemId), roleKey, updatedAt],
|
||||
)
|
||||
|
||||
return getInventoryItemById(inventoryItemId)
|
||||
})
|
||||
}
|
||||
|
||||
export async function getInventoryItemById(inventoryItemId) {
|
||||
const result = await query(
|
||||
`${INVENTORY_ITEM_SELECT}
|
||||
WHERE ii.id = $1
|
||||
LIMIT 1`,
|
||||
[Number(inventoryItemId)],
|
||||
)
|
||||
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export async function markInventoryItemDelivered(inventoryItemId, deliveredAt) {
|
||||
return withTransaction(async (client) => {
|
||||
await client.query(
|
||||
`
|
||||
UPDATE inventory_items
|
||||
SET status = 'consumed', consumed_at = $1, updated_at = $1
|
||||
WHERE id = $2
|
||||
`,
|
||||
[deliveredAt, Number(inventoryItemId)],
|
||||
)
|
||||
|
||||
await client.query(
|
||||
`
|
||||
UPDATE task_inventory_bindings
|
||||
SET binding_status = 'consumed', consumed_at = $1, updated_at = $1
|
||||
WHERE inventory_item_id = $2 AND binding_status = 'reserved'
|
||||
`,
|
||||
[deliveredAt, Number(inventoryItemId)],
|
||||
)
|
||||
|
||||
return getInventoryItemById(inventoryItemId)
|
||||
})
|
||||
}
|
||||
|
||||
export async function listInventoryItems({
|
||||
page = 1,
|
||||
pageSize = 20,
|
||||
skuCode = '',
|
||||
status = '',
|
||||
batchNo = '',
|
||||
} = {}) {
|
||||
const offset = (page - 1) * pageSize
|
||||
const filters = []
|
||||
const params = []
|
||||
|
||||
if (skuCode) {
|
||||
params.push(skuCode)
|
||||
filters.push(`ii.sku_code = $${params.length}`)
|
||||
}
|
||||
|
||||
if (status) {
|
||||
params.push(status === 'delivered' ? 'consumed' : status)
|
||||
filters.push(`ii.status = $${params.length}`)
|
||||
}
|
||||
|
||||
if (batchNo) {
|
||||
params.push(`%${batchNo}%`)
|
||||
filters.push(`ii.batch_no ILIKE $${params.length}`)
|
||||
}
|
||||
|
||||
const whereClause = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : ''
|
||||
const totalResult = await query(
|
||||
`SELECT COUNT(*)::int AS total FROM inventory_items ii ${whereClause}`,
|
||||
params,
|
||||
)
|
||||
|
||||
params.push(pageSize)
|
||||
params.push(offset)
|
||||
const itemsResult = await query(
|
||||
`${INVENTORY_ITEM_SELECT}
|
||||
${whereClause}
|
||||
ORDER BY ii.id DESC
|
||||
LIMIT $${params.length - 1} OFFSET $${params.length}`,
|
||||
params,
|
||||
)
|
||||
|
||||
return {
|
||||
items: itemsResult.rows,
|
||||
total: Number(totalResult.rows[0]?.total || 0),
|
||||
}
|
||||
}
|
||||
|
||||
export async function createInventoryItems(rows) {
|
||||
let created = 0
|
||||
|
||||
for (const row of rows) {
|
||||
const payloadJson = JSON.stringify(row.payload || { code: row.displayValue })
|
||||
const displayValue = String(row.displayValue || '').trim()
|
||||
const result = await query(
|
||||
`
|
||||
INSERT INTO inventory_items (
|
||||
batch_no,
|
||||
sku_code,
|
||||
credential_type,
|
||||
display_value,
|
||||
payload_json,
|
||||
source_type,
|
||||
status,
|
||||
invalid_reason,
|
||||
metadata_json,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5::jsonb, 'static_import', 'available', '', '{}'::jsonb, $6, $7)
|
||||
ON CONFLICT DO NOTHING
|
||||
RETURNING id
|
||||
`,
|
||||
[
|
||||
row.batchNo || '',
|
||||
row.skuCode,
|
||||
row.credentialType || 'tencent_code',
|
||||
displayValue,
|
||||
payloadJson,
|
||||
row.createdAt,
|
||||
row.updatedAt,
|
||||
],
|
||||
)
|
||||
|
||||
if (result.rows[0]?.id) {
|
||||
created += 1
|
||||
}
|
||||
}
|
||||
|
||||
return created
|
||||
}
|
||||
|
||||
export async function releaseReservedInventoryItem(inventoryItemId, updatedAt) {
|
||||
return withTransaction(async (client) => {
|
||||
await client.query(
|
||||
`
|
||||
UPDATE task_inventory_bindings
|
||||
SET binding_status = 'released', released_at = $1, updated_at = $1
|
||||
WHERE inventory_item_id = $2 AND binding_status = 'reserved'
|
||||
`,
|
||||
[updatedAt, Number(inventoryItemId)],
|
||||
)
|
||||
|
||||
await client.query(
|
||||
`
|
||||
UPDATE inventory_items
|
||||
SET status = 'available', updated_at = $1
|
||||
WHERE id = $2 AND status = 'reserved'
|
||||
`,
|
||||
[updatedAt, Number(inventoryItemId)],
|
||||
)
|
||||
|
||||
return getInventoryItemById(inventoryItemId)
|
||||
})
|
||||
}
|
||||
|
||||
export async function invalidateInventoryItem(inventoryItemId, invalidReason, updatedAt) {
|
||||
const result = await query(
|
||||
`
|
||||
UPDATE inventory_items
|
||||
SET status = 'invalid', invalid_reason = $1, updated_at = $2
|
||||
WHERE id = $3 AND status = 'available'
|
||||
RETURNING id
|
||||
`,
|
||||
[invalidReason, updatedAt, Number(inventoryItemId)],
|
||||
)
|
||||
|
||||
if (!result.rows[0]) {
|
||||
return getInventoryItemById(inventoryItemId)
|
||||
}
|
||||
|
||||
return getInventoryItemById(inventoryItemId)
|
||||
}
|
||||
Reference in New Issue
Block a user