彻底重构-1
This commit is contained in:
@@ -1,86 +1,94 @@
|
||||
import { getDb } from '../db/client.js'
|
||||
import { query } from '../db/client.js'
|
||||
|
||||
export function createAdminAuditLog(input) {
|
||||
const result = getDb().prepare(`
|
||||
INSERT INTO admin_audit_logs (
|
||||
actor_user_id,
|
||||
actor_username,
|
||||
actor_role,
|
||||
action,
|
||||
target_type,
|
||||
target_id,
|
||||
payload_json,
|
||||
created_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
input.actorUserId,
|
||||
input.actorUsername,
|
||||
input.actorRole,
|
||||
input.action,
|
||||
input.targetType,
|
||||
input.targetId,
|
||||
input.payloadJson,
|
||||
input.createdAt,
|
||||
export async function createAdminAuditLog(input) {
|
||||
const result = await query(
|
||||
`
|
||||
INSERT INTO admin_audit_logs (
|
||||
actor_user_id,
|
||||
actor_username,
|
||||
actor_role,
|
||||
action,
|
||||
target_type,
|
||||
target_id,
|
||||
payload_json,
|
||||
created_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8)
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
input.actorUserId || null,
|
||||
input.actorUsername || '',
|
||||
input.actorRole || '',
|
||||
input.action,
|
||||
input.targetType,
|
||||
input.targetId,
|
||||
input.payloadJson || '{}',
|
||||
input.createdAt,
|
||||
],
|
||||
)
|
||||
|
||||
return getAdminAuditLogById(Number(result.lastInsertRowid))
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function getAdminAuditLogById(logId) {
|
||||
return getDb().prepare('SELECT * FROM admin_audit_logs WHERE id = ? LIMIT 1').get(logId) || null
|
||||
export async function getAdminAuditLogById(logId) {
|
||||
const result = await query('SELECT * FROM admin_audit_logs WHERE id = $1 LIMIT 1', [Number(logId)])
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function listAdminAuditLogs(query = {}) {
|
||||
export async function listAdminAuditLogs(queryInput = {}) {
|
||||
const conditions = []
|
||||
const values = []
|
||||
const params = []
|
||||
|
||||
if (query.actorUsername) {
|
||||
conditions.push('actor_username = ?')
|
||||
values.push(query.actorUsername)
|
||||
if (queryInput.actorUsername) {
|
||||
params.push(queryInput.actorUsername)
|
||||
conditions.push(`actor_username = $${params.length}`)
|
||||
}
|
||||
|
||||
if (query.action) {
|
||||
conditions.push('action = ?')
|
||||
values.push(query.action)
|
||||
if (queryInput.action) {
|
||||
params.push(queryInput.action)
|
||||
conditions.push(`action = $${params.length}`)
|
||||
}
|
||||
|
||||
if (query.targetType) {
|
||||
conditions.push('target_type = ?')
|
||||
values.push(query.targetType)
|
||||
if (queryInput.targetType) {
|
||||
params.push(queryInput.targetType)
|
||||
conditions.push(`target_type = $${params.length}`)
|
||||
}
|
||||
|
||||
if (query.dateFrom) {
|
||||
conditions.push('created_at >= ?')
|
||||
values.push(query.dateFrom)
|
||||
if (queryInput.dateFrom) {
|
||||
params.push(queryInput.dateFrom)
|
||||
conditions.push(`created_at >= $${params.length}`)
|
||||
}
|
||||
|
||||
if (query.dateTo) {
|
||||
conditions.push('created_at <= ?')
|
||||
values.push(query.dateTo)
|
||||
if (queryInput.dateTo) {
|
||||
params.push(queryInput.dateTo)
|
||||
conditions.push(`created_at <= $${params.length}`)
|
||||
}
|
||||
|
||||
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''
|
||||
const page = Number(query.page) || 1
|
||||
const pageSize = Number(query.pageSize) || 20
|
||||
const page = Number(queryInput.page) || 1
|
||||
const pageSize = Number(queryInput.pageSize) || 20
|
||||
const offset = (page - 1) * pageSize
|
||||
const db = getDb()
|
||||
|
||||
const items = db.prepare(`
|
||||
SELECT *
|
||||
FROM admin_audit_logs
|
||||
${whereClause}
|
||||
ORDER BY id DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`).all(...values, pageSize, offset)
|
||||
const totalResult = await query(
|
||||
`SELECT COUNT(*)::int AS total FROM admin_audit_logs ${whereClause}`,
|
||||
params,
|
||||
)
|
||||
|
||||
const totalRow = db.prepare(`
|
||||
SELECT COUNT(*) AS total
|
||||
FROM admin_audit_logs
|
||||
${whereClause}
|
||||
`).get(...values)
|
||||
params.push(pageSize)
|
||||
params.push(offset)
|
||||
const itemsResult = await query(
|
||||
`
|
||||
SELECT *
|
||||
FROM admin_audit_logs
|
||||
${whereClause}
|
||||
ORDER BY id DESC
|
||||
LIMIT $${params.length - 1} OFFSET $${params.length}
|
||||
`,
|
||||
params,
|
||||
)
|
||||
|
||||
return {
|
||||
items,
|
||||
total: Number(totalRow?.total || 0),
|
||||
items: itemsResult.rows,
|
||||
total: Number(totalResult.rows[0]?.total || 0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,117 +1,124 @@
|
||||
import { getDb } from '../db/client.js'
|
||||
import { query } from '../db/client.js'
|
||||
|
||||
export function getAdminUserByUsername(username) {
|
||||
return getDb().prepare('SELECT * FROM admin_users WHERE username = ? LIMIT 1').get(username) || null
|
||||
export async function getAdminUserById(userId) {
|
||||
const result = await query(
|
||||
'SELECT * FROM admin_users WHERE id = $1 LIMIT 1',
|
||||
[Number(userId)],
|
||||
)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function getAdminUserById(userId) {
|
||||
return getDb().prepare('SELECT * FROM admin_users WHERE id = ? LIMIT 1').get(userId) || null
|
||||
export async function getAdminUserByUsername(username) {
|
||||
const result = await query(
|
||||
'SELECT * FROM admin_users WHERE username = $1 LIMIT 1',
|
||||
[String(username || '').trim().toLowerCase()],
|
||||
)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function createAdminUser(input) {
|
||||
const result = getDb().prepare(`
|
||||
INSERT INTO admin_users (
|
||||
username,
|
||||
password_hash,
|
||||
role,
|
||||
status,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
input.username,
|
||||
input.passwordHash,
|
||||
input.role,
|
||||
input.status,
|
||||
input.createdAt,
|
||||
input.updatedAt,
|
||||
export async function createAdminUser(input) {
|
||||
const result = await query(
|
||||
`
|
||||
INSERT INTO admin_users (
|
||||
username,
|
||||
password_hash,
|
||||
role,
|
||||
status,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
input.username,
|
||||
input.passwordHash,
|
||||
input.role,
|
||||
input.status,
|
||||
input.createdAt,
|
||||
input.updatedAt,
|
||||
],
|
||||
)
|
||||
|
||||
return getAdminUserById(Number(result.lastInsertRowid))
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function listAdminUsers(query = {}) {
|
||||
const conditions = []
|
||||
const values = []
|
||||
|
||||
if (query.username) {
|
||||
conditions.push('username LIKE ?')
|
||||
values.push(`%${query.username}%`)
|
||||
}
|
||||
|
||||
if (query.role) {
|
||||
conditions.push('role = ?')
|
||||
values.push(query.role)
|
||||
}
|
||||
|
||||
if (query.status) {
|
||||
conditions.push('status = ?')
|
||||
values.push(query.status)
|
||||
}
|
||||
|
||||
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''
|
||||
const page = Number(query.page) || 1
|
||||
const pageSize = Number(query.pageSize) || 20
|
||||
const offset = (page - 1) * pageSize
|
||||
const db = getDb()
|
||||
|
||||
const items = db.prepare(`
|
||||
SELECT *
|
||||
FROM admin_users
|
||||
${whereClause}
|
||||
ORDER BY id DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`).all(...values, pageSize, offset)
|
||||
|
||||
const totalRow = db.prepare(`
|
||||
SELECT COUNT(*) AS total
|
||||
FROM admin_users
|
||||
${whereClause}
|
||||
`).get(...values)
|
||||
|
||||
return {
|
||||
items,
|
||||
total: Number(totalRow?.total || 0),
|
||||
}
|
||||
}
|
||||
|
||||
export function updateAdminUser(userId, patch = {}) {
|
||||
const current = getAdminUserById(userId)
|
||||
|
||||
export async function updateAdminUser(userId, patch) {
|
||||
const current = await getAdminUserById(userId)
|
||||
if (!current) {
|
||||
return null
|
||||
}
|
||||
|
||||
const next = {
|
||||
username: Object.prototype.hasOwnProperty.call(patch, 'username') ? patch.username : current.username,
|
||||
password_hash: Object.prototype.hasOwnProperty.call(patch, 'password_hash') ? patch.password_hash : current.password_hash,
|
||||
role: Object.prototype.hasOwnProperty.call(patch, 'role') ? patch.role : current.role,
|
||||
status: Object.prototype.hasOwnProperty.call(patch, 'status') ? patch.status : current.status,
|
||||
updated_at: Object.prototype.hasOwnProperty.call(patch, 'updated_at') ? patch.updated_at : current.updated_at,
|
||||
}
|
||||
|
||||
getDb().prepare(`
|
||||
UPDATE admin_users
|
||||
SET username = ?, password_hash = ?, role = ?, status = ?, updated_at = ?
|
||||
WHERE id = ?
|
||||
`).run(
|
||||
next.username,
|
||||
next.password_hash,
|
||||
next.role,
|
||||
next.status,
|
||||
next.updated_at,
|
||||
userId,
|
||||
const next = { ...current, ...patch }
|
||||
const result = await query(
|
||||
`
|
||||
UPDATE admin_users
|
||||
SET
|
||||
username = $1,
|
||||
password_hash = $2,
|
||||
role = $3,
|
||||
status = $4,
|
||||
updated_at = $5
|
||||
WHERE id = $6
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
next.username,
|
||||
next.password_hash,
|
||||
next.role,
|
||||
next.status,
|
||||
next.updated_at,
|
||||
Number(userId),
|
||||
],
|
||||
)
|
||||
|
||||
return getAdminUserById(userId)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function countActiveAdminUsers() {
|
||||
const row = getDb().prepare(`
|
||||
SELECT COUNT(*) AS total
|
||||
FROM admin_users
|
||||
WHERE role = 'admin' AND status = 'active'
|
||||
`).get()
|
||||
export async function listAdminUsers({ page = 1, pageSize = 20, username = '', role = '', status = '' } = {}) {
|
||||
const offset = (page - 1) * pageSize
|
||||
const filters = []
|
||||
const params = []
|
||||
|
||||
return Number(row?.total || 0)
|
||||
if (username) {
|
||||
params.push(`%${username}%`)
|
||||
filters.push(`username ILIKE $${params.length}`)
|
||||
}
|
||||
|
||||
if (role) {
|
||||
params.push(role)
|
||||
filters.push(`role = $${params.length}`)
|
||||
}
|
||||
|
||||
if (status) {
|
||||
params.push(status)
|
||||
filters.push(`status = $${params.length}`)
|
||||
}
|
||||
|
||||
const whereClause = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : ''
|
||||
const totalResult = await query(`SELECT COUNT(*)::int AS total FROM admin_users ${whereClause}`, params)
|
||||
|
||||
params.push(pageSize)
|
||||
params.push(offset)
|
||||
const itemsResult = await query(
|
||||
`
|
||||
SELECT *
|
||||
FROM admin_users
|
||||
${whereClause}
|
||||
ORDER BY id DESC
|
||||
LIMIT $${params.length - 1} OFFSET $${params.length}
|
||||
`,
|
||||
params,
|
||||
)
|
||||
|
||||
return {
|
||||
items: itemsResult.rows,
|
||||
total: Number(totalResult.rows[0]?.total || 0),
|
||||
}
|
||||
}
|
||||
|
||||
export async function countActiveAdminUsers() {
|
||||
const result = await query(
|
||||
`SELECT COUNT(*)::int AS total FROM admin_users WHERE role = 'admin' AND status = 'active'`,
|
||||
)
|
||||
return Number(result.rows[0]?.total || 0)
|
||||
}
|
||||
|
||||
@@ -1,46 +1,118 @@
|
||||
import { getDb } from '../db/client.js'
|
||||
import { query, withTransaction } from '../db/client.js'
|
||||
|
||||
export function findFirstAvailableCdkBySkuCode(skuCode) {
|
||||
return getDb().prepare(`
|
||||
const CDK_SELECT = `
|
||||
SELECT
|
||||
ii.id,
|
||||
ii.sku_code,
|
||||
ii.batch_no,
|
||||
ii.credential_type,
|
||||
ii.display_value AS cdk_code,
|
||||
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 cdk_inventory
|
||||
WHERE sku_code = ? AND status = 'available'
|
||||
ORDER BY id ASC
|
||||
FROM task_inventory_bindings
|
||||
WHERE inventory_item_id = ii.id AND binding_status IN ('reserved', 'consumed')
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
`).get(skuCode) || null
|
||||
) tib ON TRUE
|
||||
LEFT JOIN fulfillment_tasks ft ON ft.id = tib.task_id
|
||||
`
|
||||
|
||||
export async function findFirstAvailableCdkBySkuCode(skuCode, credentialType = 'tencent_code') {
|
||||
const result = await query(
|
||||
`${CDK_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 function assignReservedCdk(cdkId, taskId, updatedAt) {
|
||||
getDb().prepare(`
|
||||
UPDATE cdk_inventory
|
||||
SET
|
||||
status = 'reserved',
|
||||
reserved_by_task_id = ?,
|
||||
updated_at = ?
|
||||
WHERE id = ? AND status = 'available'
|
||||
`).run(taskId, updatedAt, cdkId)
|
||||
export async function assignReservedCdk(cdkId, 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(cdkId)],
|
||||
)
|
||||
|
||||
return getCdkById(cdkId)
|
||||
if (!inventoryResult.rows[0]) {
|
||||
return getCdkById(cdkId)
|
||||
}
|
||||
|
||||
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(cdkId), roleKey, updatedAt],
|
||||
)
|
||||
|
||||
return getCdkById(cdkId)
|
||||
})
|
||||
}
|
||||
|
||||
export function getCdkById(cdkId) {
|
||||
return getDb().prepare('SELECT * FROM cdk_inventory WHERE id = ? LIMIT 1').get(cdkId) || null
|
||||
export async function getCdkById(cdkId) {
|
||||
const result = await query(
|
||||
`${CDK_SELECT}
|
||||
WHERE ii.id = $1
|
||||
LIMIT 1`,
|
||||
[Number(cdkId)],
|
||||
)
|
||||
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function markCdkDelivered(cdkId, deliveredAt) {
|
||||
getDb().prepare(`
|
||||
UPDATE cdk_inventory
|
||||
SET
|
||||
status = 'delivered',
|
||||
delivered_at = ?,
|
||||
updated_at = ?
|
||||
WHERE id = ?
|
||||
`).run(deliveredAt, deliveredAt, cdkId)
|
||||
export async function markCdkDelivered(cdkId, 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(cdkId)],
|
||||
)
|
||||
|
||||
return getCdkById(cdkId)
|
||||
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(cdkId)],
|
||||
)
|
||||
|
||||
return getCdkById(cdkId)
|
||||
})
|
||||
}
|
||||
|
||||
export function listCdks({
|
||||
export async function listCdks({
|
||||
page = 1,
|
||||
pageSize = 20,
|
||||
skuCode = '',
|
||||
@@ -52,63 +124,78 @@ export function listCdks({
|
||||
const params = []
|
||||
|
||||
if (skuCode) {
|
||||
filters.push('sku_code = ?')
|
||||
params.push(skuCode)
|
||||
filters.push(`ii.sku_code = $${params.length}`)
|
||||
}
|
||||
|
||||
if (status) {
|
||||
filters.push('status = ?')
|
||||
params.push(status)
|
||||
params.push(status === 'delivered' ? 'consumed' : status)
|
||||
filters.push(`ii.status = $${params.length}`)
|
||||
}
|
||||
|
||||
if (batchNo) {
|
||||
filters.push('batch_no LIKE ?')
|
||||
params.push(`%${batchNo}%`)
|
||||
filters.push(`ii.batch_no ILIKE $${params.length}`)
|
||||
}
|
||||
|
||||
const whereClause = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : ''
|
||||
const db = getDb()
|
||||
const totalRow = db.prepare(`
|
||||
SELECT COUNT(*) AS total
|
||||
FROM cdk_inventory
|
||||
${whereClause}
|
||||
`).get(...params)
|
||||
const totalResult = await query(
|
||||
`SELECT COUNT(*)::int AS total FROM inventory_items ii ${whereClause}`,
|
||||
params,
|
||||
)
|
||||
|
||||
const items = db.prepare(`
|
||||
SELECT *
|
||||
FROM cdk_inventory
|
||||
${whereClause}
|
||||
ORDER BY id DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`).all(...params, pageSize, offset)
|
||||
params.push(pageSize)
|
||||
params.push(offset)
|
||||
const itemsResult = await query(
|
||||
`${CDK_SELECT}
|
||||
${whereClause}
|
||||
ORDER BY ii.id DESC
|
||||
LIMIT $${params.length - 1} OFFSET $${params.length}`,
|
||||
params,
|
||||
)
|
||||
|
||||
return {
|
||||
items,
|
||||
total: Number(totalRow?.total || 0),
|
||||
items: itemsResult.rows,
|
||||
total: Number(totalResult.rows[0]?.total || 0),
|
||||
}
|
||||
}
|
||||
|
||||
export function createCdks(rows) {
|
||||
const db = getDb()
|
||||
const stmt = db.prepare(`
|
||||
INSERT OR IGNORE INTO cdk_inventory (
|
||||
batch_no,
|
||||
sku_code,
|
||||
cdk_code,
|
||||
status,
|
||||
reserved_by_task_id,
|
||||
invalid_reason,
|
||||
delivered_at,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (?, ?, ?, 'available', NULL, '', NULL, ?, ?)
|
||||
`)
|
||||
|
||||
export async function createCdks(rows) {
|
||||
let created = 0
|
||||
|
||||
for (const row of rows) {
|
||||
const result = stmt.run(row.batchNo, row.skuCode, row.cdkCode, row.createdAt, row.updatedAt)
|
||||
if (Number(result.changes || 0) > 0) {
|
||||
const payloadJson = JSON.stringify(row.payload || { code: row.cdkCode })
|
||||
const displayValue = String(row.cdkCode || 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
|
||||
}
|
||||
}
|
||||
@@ -116,28 +203,44 @@ export function createCdks(rows) {
|
||||
return created
|
||||
}
|
||||
|
||||
export function releaseReservedCdk(cdkId, updatedAt) {
|
||||
getDb().prepare(`
|
||||
UPDATE cdk_inventory
|
||||
SET
|
||||
status = 'available',
|
||||
reserved_by_task_id = NULL,
|
||||
updated_at = ?
|
||||
WHERE id = ? AND status = 'reserved'
|
||||
`).run(updatedAt, cdkId)
|
||||
export async function releaseReservedCdk(cdkId, 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(cdkId)],
|
||||
)
|
||||
|
||||
return getCdkById(cdkId)
|
||||
await client.query(
|
||||
`
|
||||
UPDATE inventory_items
|
||||
SET status = 'available', updated_at = $1
|
||||
WHERE id = $2 AND status = 'reserved'
|
||||
`,
|
||||
[updatedAt, Number(cdkId)],
|
||||
)
|
||||
|
||||
return getCdkById(cdkId)
|
||||
})
|
||||
}
|
||||
|
||||
export function invalidateCdk(cdkId, invalidReason, updatedAt) {
|
||||
getDb().prepare(`
|
||||
UPDATE cdk_inventory
|
||||
SET
|
||||
status = 'invalid',
|
||||
invalid_reason = ?,
|
||||
updated_at = ?
|
||||
WHERE id = ? AND status = 'available'
|
||||
`).run(invalidReason, updatedAt, cdkId)
|
||||
export async function invalidateCdk(cdkId, 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(cdkId)],
|
||||
)
|
||||
|
||||
if (!result.rows[0]) {
|
||||
return getCdkById(cdkId)
|
||||
}
|
||||
|
||||
return getCdkById(cdkId)
|
||||
}
|
||||
|
||||
@@ -1,69 +1,77 @@
|
||||
import { getDb } from '../db/client.js'
|
||||
import { query } from '../db/client.js'
|
||||
|
||||
export function createClaimToken(input) {
|
||||
const result = getDb().prepare(`
|
||||
INSERT INTO claim_tokens (
|
||||
task_id,
|
||||
token,
|
||||
status,
|
||||
expired_at,
|
||||
used_at,
|
||||
max_use_count,
|
||||
used_count,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
input.taskId,
|
||||
input.token,
|
||||
input.status,
|
||||
input.expiredAt,
|
||||
input.usedAt,
|
||||
input.maxUseCount,
|
||||
input.usedCount,
|
||||
input.createdAt,
|
||||
input.updatedAt,
|
||||
export async function createClaimToken(input) {
|
||||
const result = await query(
|
||||
`
|
||||
INSERT INTO claim_tokens (
|
||||
task_id,
|
||||
token,
|
||||
status,
|
||||
expired_at,
|
||||
used_at,
|
||||
max_use_count,
|
||||
used_count,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
input.taskId,
|
||||
input.token,
|
||||
input.status,
|
||||
input.expiredAt,
|
||||
input.usedAt || null,
|
||||
input.maxUseCount,
|
||||
input.usedCount,
|
||||
input.createdAt,
|
||||
input.updatedAt,
|
||||
],
|
||||
)
|
||||
|
||||
return getClaimTokenById(Number(result.lastInsertRowid))
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function getClaimTokenById(tokenId) {
|
||||
return getDb().prepare('SELECT * FROM claim_tokens WHERE id = ? LIMIT 1').get(tokenId) || null
|
||||
export async function getClaimTokenById(tokenId) {
|
||||
const result = await query('SELECT * FROM claim_tokens WHERE id = $1 LIMIT 1', [Number(tokenId)])
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function findClaimTokenByToken(token) {
|
||||
return getDb().prepare('SELECT * FROM claim_tokens WHERE token = ? LIMIT 1').get(token) || null
|
||||
export async function findClaimTokenByToken(token) {
|
||||
const result = await query('SELECT * FROM claim_tokens WHERE token = $1 LIMIT 1', [String(token || '').trim()])
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function updateClaimToken(tokenId, patch) {
|
||||
const current = getClaimTokenById(tokenId)
|
||||
|
||||
export async function updateClaimToken(tokenId, patch) {
|
||||
const current = await getClaimTokenById(tokenId)
|
||||
if (!current) {
|
||||
return null
|
||||
}
|
||||
|
||||
const next = { ...current, ...patch }
|
||||
|
||||
getDb().prepare(`
|
||||
UPDATE claim_tokens
|
||||
SET
|
||||
status = ?,
|
||||
expired_at = ?,
|
||||
used_at = ?,
|
||||
max_use_count = ?,
|
||||
used_count = ?,
|
||||
updated_at = ?
|
||||
WHERE id = ?
|
||||
`).run(
|
||||
next.status,
|
||||
next.expired_at,
|
||||
next.used_at,
|
||||
next.max_use_count,
|
||||
next.used_count,
|
||||
next.updated_at,
|
||||
tokenId,
|
||||
const result = await query(
|
||||
`
|
||||
UPDATE claim_tokens
|
||||
SET
|
||||
status = $1,
|
||||
expired_at = $2,
|
||||
used_at = $3,
|
||||
max_use_count = $4,
|
||||
used_count = $5,
|
||||
updated_at = $6
|
||||
WHERE id = $7
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
next.status,
|
||||
next.expired_at,
|
||||
next.used_at,
|
||||
next.max_use_count,
|
||||
next.used_count,
|
||||
next.updated_at,
|
||||
Number(tokenId),
|
||||
],
|
||||
)
|
||||
|
||||
return getClaimTokenById(tokenId)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
import { query, withTransaction } from '../db/client.js'
|
||||
|
||||
export async function getFulfillmentProfileByKey(profileKey) {
|
||||
const result = await query(
|
||||
'SELECT * FROM fulfillment_profiles WHERE profile_key = $1 LIMIT 1',
|
||||
[String(profileKey || '').trim()],
|
||||
)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export async function upsertFulfillmentProfile(input) {
|
||||
const result = await query(
|
||||
`
|
||||
INSERT INTO fulfillment_profiles (
|
||||
profile_key,
|
||||
name,
|
||||
executor_key,
|
||||
requires_claim,
|
||||
auto_dispatch,
|
||||
inventory_strategy,
|
||||
config_json,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8, $9)
|
||||
ON CONFLICT (profile_key) DO UPDATE
|
||||
SET
|
||||
name = EXCLUDED.name,
|
||||
executor_key = EXCLUDED.executor_key,
|
||||
requires_claim = EXCLUDED.requires_claim,
|
||||
auto_dispatch = EXCLUDED.auto_dispatch,
|
||||
inventory_strategy = EXCLUDED.inventory_strategy,
|
||||
config_json = EXCLUDED.config_json,
|
||||
updated_at = EXCLUDED.updated_at
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
input.profileKey,
|
||||
input.name,
|
||||
input.executorKey,
|
||||
Boolean(input.requiresClaim),
|
||||
Boolean(input.autoDispatch),
|
||||
input.inventoryStrategy || 'static_pool',
|
||||
input.configJson || '{}',
|
||||
input.createdAt,
|
||||
input.updatedAt,
|
||||
],
|
||||
)
|
||||
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export async function replaceFulfillmentProfileRequirements(profileId, requirements = [], timestamp) {
|
||||
await withTransaction(async (client) => {
|
||||
await client.query(
|
||||
'DELETE FROM fulfillment_profile_requirements WHERE profile_id = $1',
|
||||
[Number(profileId)],
|
||||
)
|
||||
|
||||
for (const requirement of requirements) {
|
||||
await client.query(
|
||||
`
|
||||
INSERT INTO fulfillment_profile_requirements (
|
||||
profile_id,
|
||||
role_key,
|
||||
credential_type,
|
||||
quantity_per_unit,
|
||||
is_required,
|
||||
config_json,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7, $8)
|
||||
`,
|
||||
[
|
||||
Number(profileId),
|
||||
requirement.roleKey,
|
||||
requirement.credentialType,
|
||||
Number(requirement.quantityPerUnit || 1),
|
||||
requirement.isRequired !== false,
|
||||
requirement.configJson || '{}',
|
||||
timestamp,
|
||||
timestamp,
|
||||
],
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function listFulfillmentProfileRequirements(profileId) {
|
||||
const result = await query(
|
||||
`
|
||||
SELECT *
|
||||
FROM fulfillment_profile_requirements
|
||||
WHERE profile_id = $1
|
||||
ORDER BY id ASC
|
||||
`,
|
||||
[Number(profileId)],
|
||||
)
|
||||
|
||||
return result.rows
|
||||
}
|
||||
|
||||
export async function upsertSkuFulfillmentBinding(input) {
|
||||
const existing = await query(
|
||||
`
|
||||
SELECT *
|
||||
FROM sku_fulfillment_bindings
|
||||
WHERE sku_code = $1 AND provider = $2 AND platform = $3 AND shop_id = $4
|
||||
LIMIT 1
|
||||
`,
|
||||
[input.skuCode, input.provider || '', input.platform || '', input.shopId || ''],
|
||||
)
|
||||
|
||||
if (!existing.rows[0]) {
|
||||
const inserted = await query(
|
||||
`
|
||||
INSERT INTO sku_fulfillment_bindings (
|
||||
sku_code,
|
||||
provider,
|
||||
platform,
|
||||
shop_id,
|
||||
profile_id,
|
||||
enabled,
|
||||
priority,
|
||||
config_json,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9, $10)
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
input.skuCode,
|
||||
input.provider || '',
|
||||
input.platform || '',
|
||||
input.shopId || '',
|
||||
Number(input.profileId),
|
||||
input.enabled !== false,
|
||||
Number(input.priority || 100),
|
||||
input.configJson || '{}',
|
||||
input.createdAt,
|
||||
input.updatedAt,
|
||||
],
|
||||
)
|
||||
|
||||
return inserted.rows[0] || null
|
||||
}
|
||||
|
||||
const updated = await query(
|
||||
`
|
||||
UPDATE sku_fulfillment_bindings
|
||||
SET
|
||||
profile_id = $1,
|
||||
enabled = $2,
|
||||
priority = $3,
|
||||
config_json = $4::jsonb,
|
||||
updated_at = $5
|
||||
WHERE id = $6
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
Number(input.profileId),
|
||||
input.enabled !== false,
|
||||
Number(input.priority || 100),
|
||||
input.configJson || '{}',
|
||||
input.updatedAt,
|
||||
Number(existing.rows[0].id),
|
||||
],
|
||||
)
|
||||
|
||||
return updated.rows[0] || null
|
||||
}
|
||||
|
||||
export async function resolveFulfillmentBinding({ skuCode, provider = '', platform = '', shopId = '' }) {
|
||||
const result = await query(
|
||||
`
|
||||
SELECT
|
||||
sfb.*,
|
||||
fp.profile_key,
|
||||
fp.name AS profile_name,
|
||||
fp.executor_key,
|
||||
fp.requires_claim,
|
||||
fp.auto_dispatch,
|
||||
fp.inventory_strategy,
|
||||
fp.config_json AS profile_config_json
|
||||
FROM sku_fulfillment_bindings sfb
|
||||
JOIN fulfillment_profiles fp ON fp.id = sfb.profile_id
|
||||
WHERE sfb.enabled = TRUE
|
||||
AND sfb.sku_code = $1
|
||||
AND (sfb.provider = '' OR sfb.provider = $2)
|
||||
AND (sfb.platform = '' OR sfb.platform = $3)
|
||||
AND (sfb.shop_id = '' OR sfb.shop_id = $4)
|
||||
ORDER BY
|
||||
CASE WHEN sfb.shop_id = '' THEN 1 ELSE 0 END,
|
||||
CASE WHEN sfb.platform = '' THEN 1 ELSE 0 END,
|
||||
CASE WHEN sfb.provider = '' THEN 1 ELSE 0 END,
|
||||
sfb.priority ASC,
|
||||
sfb.id ASC
|
||||
LIMIT 1
|
||||
`,
|
||||
[skuCode, provider, platform, shopId],
|
||||
)
|
||||
|
||||
return result.rows[0] || null
|
||||
}
|
||||
@@ -1,105 +1,117 @@
|
||||
import { getDb } from '../db/client.js'
|
||||
import { query } from '../db/client.js'
|
||||
|
||||
export function createMessageDelivery(input) {
|
||||
const result = getDb().prepare(`
|
||||
INSERT INTO message_deliveries (
|
||||
provider,
|
||||
platform,
|
||||
shop_id,
|
||||
shop_name,
|
||||
channel,
|
||||
order_id,
|
||||
task_id,
|
||||
platform_order_id,
|
||||
recipient_key,
|
||||
message_content,
|
||||
claim_url,
|
||||
status,
|
||||
request_url,
|
||||
request_headers_json,
|
||||
request_body_json,
|
||||
response_status,
|
||||
response_json,
|
||||
error_message,
|
||||
sent_at,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
input.provider,
|
||||
input.platform,
|
||||
input.shopId,
|
||||
input.shopName,
|
||||
input.channel,
|
||||
input.orderId,
|
||||
input.taskId,
|
||||
input.platformOrderId,
|
||||
input.recipientKey,
|
||||
input.messageContent,
|
||||
input.claimUrl,
|
||||
input.status,
|
||||
input.requestUrl,
|
||||
input.requestHeadersJson,
|
||||
input.requestBodyJson,
|
||||
input.responseStatus,
|
||||
input.responseJson,
|
||||
input.errorMessage,
|
||||
input.sentAt,
|
||||
input.createdAt,
|
||||
input.updatedAt,
|
||||
export async function createMessageDelivery(input) {
|
||||
const result = await query(
|
||||
`
|
||||
INSERT INTO message_deliveries (
|
||||
provider,
|
||||
platform,
|
||||
shop_id,
|
||||
shop_name,
|
||||
channel,
|
||||
order_id,
|
||||
task_id,
|
||||
platform_order_id,
|
||||
recipient_key,
|
||||
message_content,
|
||||
claim_url,
|
||||
status,
|
||||
request_url,
|
||||
request_headers_json,
|
||||
request_body_json,
|
||||
response_status,
|
||||
response_json,
|
||||
error_message,
|
||||
sent_at,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14::jsonb, $15::jsonb, $16, $17::jsonb, $18, $19, $20, $21)
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
input.provider,
|
||||
input.platform,
|
||||
input.shopId || '',
|
||||
input.shopName || '',
|
||||
input.channel,
|
||||
input.orderId || null,
|
||||
input.taskId || null,
|
||||
input.platformOrderId || '',
|
||||
input.recipientKey || '',
|
||||
input.messageContent || '',
|
||||
input.claimUrl || '',
|
||||
input.status || 'pending',
|
||||
input.requestUrl || '',
|
||||
input.requestHeadersJson || '{}',
|
||||
input.requestBodyJson || '{}',
|
||||
Number(input.responseStatus || 0),
|
||||
input.responseJson || '{}',
|
||||
input.errorMessage || '',
|
||||
input.sentAt || null,
|
||||
input.createdAt,
|
||||
input.updatedAt,
|
||||
],
|
||||
)
|
||||
|
||||
return getMessageDeliveryById(Number(result.lastInsertRowid))
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function getMessageDeliveryById(deliveryId) {
|
||||
return getDb().prepare('SELECT * FROM message_deliveries WHERE id = ? LIMIT 1').get(deliveryId) || null
|
||||
export async function getMessageDeliveryById(deliveryId) {
|
||||
const result = await query('SELECT * FROM message_deliveries WHERE id = $1 LIMIT 1', [Number(deliveryId)])
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function updateMessageDelivery(deliveryId, patch = {}) {
|
||||
const current = getMessageDeliveryById(deliveryId)
|
||||
|
||||
export async function updateMessageDelivery(deliveryId, patch = {}) {
|
||||
const current = await getMessageDeliveryById(deliveryId)
|
||||
if (!current) {
|
||||
return null
|
||||
}
|
||||
|
||||
const next = { ...current, ...patch }
|
||||
|
||||
getDb().prepare(`
|
||||
UPDATE message_deliveries
|
||||
SET
|
||||
status = ?,
|
||||
request_url = ?,
|
||||
request_headers_json = ?,
|
||||
request_body_json = ?,
|
||||
response_status = ?,
|
||||
response_json = ?,
|
||||
error_message = ?,
|
||||
sent_at = ?,
|
||||
updated_at = ?
|
||||
WHERE id = ?
|
||||
`).run(
|
||||
next.status,
|
||||
next.request_url,
|
||||
next.request_headers_json,
|
||||
next.request_body_json,
|
||||
next.response_status,
|
||||
next.response_json,
|
||||
next.error_message,
|
||||
next.sent_at,
|
||||
next.updated_at,
|
||||
deliveryId,
|
||||
const result = await query(
|
||||
`
|
||||
UPDATE message_deliveries
|
||||
SET
|
||||
status = $1,
|
||||
request_url = $2,
|
||||
request_headers_json = $3::jsonb,
|
||||
request_body_json = $4::jsonb,
|
||||
response_status = $5,
|
||||
response_json = $6::jsonb,
|
||||
error_message = $7,
|
||||
sent_at = $8,
|
||||
updated_at = $9
|
||||
WHERE id = $10
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
next.status,
|
||||
next.request_url || '',
|
||||
next.request_headers_json || '{}',
|
||||
next.request_body_json || '{}',
|
||||
Number(next.response_status || 0),
|
||||
next.response_json || '{}',
|
||||
next.error_message || '',
|
||||
next.sent_at || null,
|
||||
next.updated_at,
|
||||
Number(deliveryId),
|
||||
],
|
||||
)
|
||||
|
||||
return getMessageDeliveryById(deliveryId)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function findLatestSuccessfulMessageDeliveryByTask(taskId, channel) {
|
||||
return getDb().prepare(`
|
||||
SELECT *
|
||||
FROM message_deliveries
|
||||
WHERE task_id = ? AND channel = ? AND status = 'success'
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
`).get(taskId, channel) || null
|
||||
export async function findLatestSuccessfulMessageDeliveryByTask(taskId, channel) {
|
||||
const result = await query(
|
||||
`
|
||||
SELECT *
|
||||
FROM message_deliveries
|
||||
WHERE task_id = $1 AND channel = $2 AND status = 'success'
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
`,
|
||||
[Number(taskId), channel],
|
||||
)
|
||||
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
@@ -1,80 +1,53 @@
|
||||
import { getDb } from '../db/client.js'
|
||||
import { query } from '../db/client.js'
|
||||
|
||||
export function listOrderItemsByOrderId(orderId) {
|
||||
return getDb().prepare(`
|
||||
SELECT *
|
||||
FROM order_items
|
||||
WHERE order_id = ?
|
||||
ORDER BY id ASC
|
||||
`).all(orderId)
|
||||
export async function listOrderItemsByOrderId(orderId) {
|
||||
const result = await query(
|
||||
`
|
||||
SELECT *
|
||||
FROM order_items
|
||||
WHERE order_id = $1
|
||||
ORDER BY id ASC
|
||||
`,
|
||||
[Number(orderId)],
|
||||
)
|
||||
|
||||
return result.rows
|
||||
}
|
||||
|
||||
export function replaceOrderItems(orderId, items) {
|
||||
const db = getDb()
|
||||
const existingItems = listOrderItemsByOrderId(orderId)
|
||||
const updateStatement = db.prepare(`
|
||||
UPDATE order_items
|
||||
SET
|
||||
sku_code = ?,
|
||||
sku_name = ?,
|
||||
quantity = ?,
|
||||
spec_json = ?,
|
||||
delivery_mode = ?,
|
||||
updated_at = ?
|
||||
WHERE id = ?
|
||||
`)
|
||||
const insertStatement = db.prepare(`
|
||||
INSERT INTO order_items (
|
||||
order_id,
|
||||
sku_code,
|
||||
sku_name,
|
||||
quantity,
|
||||
spec_json,
|
||||
delivery_mode,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`)
|
||||
export async function replaceOrderItems(orderId, items) {
|
||||
await query('DELETE FROM order_items WHERE order_id = $1', [Number(orderId)])
|
||||
|
||||
for (let index = 0; index < items.length; index += 1) {
|
||||
const item = items[index]
|
||||
const existingItem = existingItems[index] || null
|
||||
|
||||
if (existingItem) {
|
||||
updateStatement.run(
|
||||
for (const item of items) {
|
||||
await query(
|
||||
`
|
||||
INSERT INTO order_items (
|
||||
order_id,
|
||||
sku_code,
|
||||
sku_name,
|
||||
quantity,
|
||||
spec_json,
|
||||
item_snapshot_json,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5::jsonb, $6::jsonb, $7, $8)
|
||||
`,
|
||||
[
|
||||
Number(orderId),
|
||||
item.skuCode,
|
||||
item.skuName,
|
||||
item.quantity,
|
||||
item.specJson,
|
||||
item.deliveryMode,
|
||||
item.specJson || '{}',
|
||||
item.itemSnapshotJson || item.specJson || '{}',
|
||||
item.createdAt,
|
||||
item.updatedAt,
|
||||
existingItem.id,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
insertStatement.run(
|
||||
orderId,
|
||||
item.skuCode,
|
||||
item.skuName,
|
||||
item.quantity,
|
||||
item.specJson,
|
||||
item.deliveryMode,
|
||||
item.createdAt,
|
||||
item.updatedAt,
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
if (existingItems.length > items.length) {
|
||||
const redundantIds = existingItems.slice(items.length).map((item) => item.id)
|
||||
const placeholders = redundantIds.map(() => '?').join(', ')
|
||||
|
||||
db.prepare(`DELETE FROM order_items WHERE order_id = ? AND id IN (${placeholders})`).run(orderId, ...redundantIds)
|
||||
}
|
||||
|
||||
return listOrderItemsByOrderId(orderId)
|
||||
}
|
||||
|
||||
export function getOrderItemById(orderItemId) {
|
||||
return getDb().prepare('SELECT * FROM order_items WHERE id = ? LIMIT 1').get(orderItemId) || null
|
||||
export async function getOrderItemById(orderItemId) {
|
||||
const result = await query('SELECT * FROM order_items WHERE id = $1 LIMIT 1', [Number(orderItemId)])
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
@@ -1,101 +1,115 @@
|
||||
import { getDb } from '../db/client.js'
|
||||
import { query } from '../db/client.js'
|
||||
|
||||
export function findOrderByPlatformOrderId({ provider = 'agiso', platform, shopId = '', platformOrderId }) {
|
||||
return getDb().prepare(`
|
||||
SELECT *
|
||||
FROM orders
|
||||
WHERE provider = ? AND platform = ? AND shop_id = ? AND platform_order_id = ?
|
||||
LIMIT 1
|
||||
`).get(provider, platform, shopId, platformOrderId) || null
|
||||
}
|
||||
|
||||
export function createOrder(input) {
|
||||
const result = getDb().prepare(`
|
||||
INSERT INTO orders (
|
||||
provider,
|
||||
platform,
|
||||
shop_id,
|
||||
shop_name,
|
||||
platform_order_id,
|
||||
order_status,
|
||||
pay_status,
|
||||
buyer_id,
|
||||
buyer_name,
|
||||
receiver_contact,
|
||||
total_amount,
|
||||
currency,
|
||||
raw_payload_json,
|
||||
paid_at,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
input.provider,
|
||||
input.platform,
|
||||
input.shopId,
|
||||
input.shopName,
|
||||
input.platformOrderId,
|
||||
input.orderStatus,
|
||||
input.payStatus,
|
||||
input.buyerId,
|
||||
input.buyerName,
|
||||
input.receiverContact,
|
||||
input.totalAmount,
|
||||
input.currency,
|
||||
input.rawPayloadJson,
|
||||
input.paidAt,
|
||||
input.createdAt,
|
||||
input.updatedAt,
|
||||
export async function findOrderByPlatformOrderId({ provider = 'agiso', platform, shopId = '', platformOrderId }) {
|
||||
const result = await query(
|
||||
`
|
||||
SELECT *
|
||||
FROM orders
|
||||
WHERE provider = $1 AND platform = $2 AND shop_id = $3 AND platform_order_id = $4
|
||||
LIMIT 1
|
||||
`,
|
||||
[provider, platform, shopId, platformOrderId],
|
||||
)
|
||||
|
||||
return getOrderById(Number(result.lastInsertRowid))
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function updateOrder(orderId, input) {
|
||||
getDb().prepare(`
|
||||
UPDATE orders
|
||||
SET
|
||||
provider = ?,
|
||||
platform = ?,
|
||||
shop_id = ?,
|
||||
shop_name = ?,
|
||||
order_status = ?,
|
||||
pay_status = ?,
|
||||
buyer_id = ?,
|
||||
buyer_name = ?,
|
||||
receiver_contact = ?,
|
||||
total_amount = ?,
|
||||
currency = ?,
|
||||
raw_payload_json = ?,
|
||||
paid_at = ?,
|
||||
updated_at = ?
|
||||
WHERE id = ?
|
||||
`).run(
|
||||
input.provider,
|
||||
input.platform,
|
||||
input.shopId,
|
||||
input.shopName,
|
||||
input.orderStatus,
|
||||
input.payStatus,
|
||||
input.buyerId,
|
||||
input.buyerName,
|
||||
input.receiverContact,
|
||||
input.totalAmount,
|
||||
input.currency,
|
||||
input.rawPayloadJson,
|
||||
input.paidAt,
|
||||
input.updatedAt,
|
||||
orderId,
|
||||
export async function createOrder(input) {
|
||||
const result = await query(
|
||||
`
|
||||
INSERT INTO orders (
|
||||
provider,
|
||||
platform,
|
||||
shop_id,
|
||||
shop_name,
|
||||
platform_order_id,
|
||||
order_status,
|
||||
pay_status,
|
||||
buyer_id,
|
||||
buyer_name,
|
||||
receiver_contact,
|
||||
total_amount,
|
||||
currency,
|
||||
raw_payload_json,
|
||||
paid_at,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13::jsonb, $14, $15, $16)
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
input.provider,
|
||||
input.platform,
|
||||
input.shopId,
|
||||
input.shopName,
|
||||
input.platformOrderId,
|
||||
input.orderStatus,
|
||||
input.payStatus,
|
||||
input.buyerId,
|
||||
input.buyerName,
|
||||
input.receiverContact,
|
||||
input.totalAmount,
|
||||
input.currency,
|
||||
input.rawPayloadJson || '{}',
|
||||
input.paidAt || null,
|
||||
input.createdAt,
|
||||
input.updatedAt,
|
||||
],
|
||||
)
|
||||
|
||||
return getOrderById(orderId)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function getOrderById(orderId) {
|
||||
return getDb().prepare('SELECT * FROM orders WHERE id = ? LIMIT 1').get(orderId) || null
|
||||
export async function updateOrder(orderId, input) {
|
||||
const result = await query(
|
||||
`
|
||||
UPDATE orders
|
||||
SET
|
||||
provider = $1,
|
||||
platform = $2,
|
||||
shop_id = $3,
|
||||
shop_name = $4,
|
||||
order_status = $5,
|
||||
pay_status = $6,
|
||||
buyer_id = $7,
|
||||
buyer_name = $8,
|
||||
receiver_contact = $9,
|
||||
total_amount = $10,
|
||||
currency = $11,
|
||||
raw_payload_json = $12::jsonb,
|
||||
paid_at = $13,
|
||||
updated_at = $14
|
||||
WHERE id = $15
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
input.provider,
|
||||
input.platform,
|
||||
input.shopId,
|
||||
input.shopName,
|
||||
input.orderStatus,
|
||||
input.payStatus,
|
||||
input.buyerId,
|
||||
input.buyerName,
|
||||
input.receiverContact,
|
||||
input.totalAmount,
|
||||
input.currency,
|
||||
input.rawPayloadJson || '{}',
|
||||
input.paidAt || null,
|
||||
input.updatedAt,
|
||||
Number(orderId),
|
||||
],
|
||||
)
|
||||
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function listOrders({
|
||||
export async function getOrderById(orderId) {
|
||||
const result = await query('SELECT * FROM orders WHERE id = $1 LIMIT 1', [Number(orderId)])
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export async function listOrders({
|
||||
page = 1,
|
||||
pageSize = 20,
|
||||
platformOrderId = '',
|
||||
@@ -109,50 +123,50 @@ export function listOrders({
|
||||
const params = []
|
||||
|
||||
if (platformOrderId) {
|
||||
filters.push('o.platform_order_id LIKE ?')
|
||||
params.push(`%${platformOrderId}%`)
|
||||
filters.push(`o.platform_order_id ILIKE $${params.length}`)
|
||||
}
|
||||
|
||||
if (payStatus) {
|
||||
filters.push('o.pay_status = ?')
|
||||
params.push(payStatus)
|
||||
filters.push(`o.pay_status = $${params.length}`)
|
||||
}
|
||||
|
||||
if (skuCode) {
|
||||
filters.push('EXISTS (SELECT 1 FROM order_items oi WHERE oi.order_id = o.id AND oi.sku_code = ?)')
|
||||
params.push(skuCode)
|
||||
filters.push(`EXISTS (SELECT 1 FROM order_items oi WHERE oi.order_id = o.id AND oi.sku_code = $${params.length})`)
|
||||
}
|
||||
|
||||
if (dateFrom) {
|
||||
filters.push('o.created_at >= ?')
|
||||
params.push(dateFrom)
|
||||
filters.push(`o.created_at >= $${params.length}`)
|
||||
}
|
||||
|
||||
if (dateTo) {
|
||||
filters.push('o.created_at <= ?')
|
||||
params.push(dateTo)
|
||||
filters.push(`o.created_at <= $${params.length}`)
|
||||
}
|
||||
|
||||
const whereClause = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : ''
|
||||
const db = getDb()
|
||||
const totalRow = db.prepare(`
|
||||
SELECT COUNT(*) AS total
|
||||
FROM orders o
|
||||
${whereClause}
|
||||
`).get(...params)
|
||||
const totalResult = await query(`SELECT COUNT(*)::int AS total FROM orders o ${whereClause}`, params)
|
||||
|
||||
const items = db.prepare(`
|
||||
SELECT
|
||||
o.*,
|
||||
(SELECT COUNT(*) FROM delivery_tasks dt WHERE dt.order_id = o.id) AS task_count
|
||||
FROM orders o
|
||||
${whereClause}
|
||||
ORDER BY o.id DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`).all(...params, pageSize, offset)
|
||||
params.push(pageSize)
|
||||
params.push(offset)
|
||||
const itemsResult = await query(
|
||||
`
|
||||
SELECT
|
||||
o.*,
|
||||
(SELECT COUNT(*)::int FROM fulfillment_tasks ft WHERE ft.order_id = o.id) AS task_count
|
||||
FROM orders o
|
||||
${whereClause}
|
||||
ORDER BY o.id DESC
|
||||
LIMIT $${params.length - 1} OFFSET $${params.length}
|
||||
`,
|
||||
params,
|
||||
)
|
||||
|
||||
return {
|
||||
items,
|
||||
total: Number(totalRow?.total || 0),
|
||||
items: itemsResult.rows,
|
||||
total: Number(totalResult.rows[0]?.total || 0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,152 +1,227 @@
|
||||
import { getDb } from '../db/client.js'
|
||||
import { query, withTransaction } from '../db/client.js'
|
||||
|
||||
export function listTasksByOrderId(orderId) {
|
||||
return getDb().prepare(`
|
||||
SELECT *
|
||||
FROM delivery_tasks
|
||||
WHERE order_id = ?
|
||||
ORDER BY id ASC
|
||||
`).all(orderId)
|
||||
const TASK_FIELDS = `
|
||||
ft.*,
|
||||
ct.id AS claim_token_id,
|
||||
ct.token AS claim_token,
|
||||
ct.status AS claim_token_status,
|
||||
ct.expired_at AS claim_token_expired_at,
|
||||
ctx.browser_session_id,
|
||||
ctx.login_type,
|
||||
ctx.nickname,
|
||||
ctx.role_id,
|
||||
ctx.role_name,
|
||||
ctx.area,
|
||||
ctx.partition_name,
|
||||
ctx.screenshot_path,
|
||||
ctx.artifacts_json,
|
||||
ctx.state_json,
|
||||
inv.inventory_item_id AS reserved_cdk_id,
|
||||
inv.display_value AS cdk_code,
|
||||
inv.credential_type AS cdk_credential_type,
|
||||
inv.binding_status AS inventory_binding_status
|
||||
`
|
||||
|
||||
const TASK_JOINS = `
|
||||
FROM fulfillment_tasks ft
|
||||
LEFT JOIN claim_tokens ct ON ct.task_id = ft.id
|
||||
LEFT JOIN tencent_browser_contexts ctx ON ctx.task_id = ft.id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT
|
||||
tib.inventory_item_id,
|
||||
tib.binding_status,
|
||||
ii.display_value,
|
||||
ii.credential_type
|
||||
FROM task_inventory_bindings tib
|
||||
JOIN inventory_items ii ON ii.id = tib.inventory_item_id
|
||||
WHERE tib.task_id = ft.id AND tib.binding_status IN ('reserved', 'consumed')
|
||||
ORDER BY tib.id ASC
|
||||
LIMIT 1
|
||||
) inv ON TRUE
|
||||
`
|
||||
|
||||
function buildTaskSelect(extraFields = '') {
|
||||
const normalizedExtra = String(extraFields || '').trim()
|
||||
const fieldSql = normalizedExtra ? `${TASK_FIELDS}, ${normalizedExtra}` : TASK_FIELDS
|
||||
return `SELECT ${fieldSql} ${TASK_JOINS}`
|
||||
}
|
||||
|
||||
export function createTask(input) {
|
||||
const result = getDb().prepare(`
|
||||
INSERT INTO delivery_tasks (
|
||||
order_id,
|
||||
order_item_id,
|
||||
platform_order_id,
|
||||
task_no,
|
||||
task_status,
|
||||
login_type,
|
||||
claim_token_id,
|
||||
reserved_cdk_id,
|
||||
browser_session_id,
|
||||
nickname,
|
||||
role_id,
|
||||
role_name,
|
||||
area,
|
||||
partition_name,
|
||||
result_code,
|
||||
result_message,
|
||||
screenshot_path,
|
||||
artifacts_json,
|
||||
last_error,
|
||||
retry_count,
|
||||
expires_at,
|
||||
claimed_at,
|
||||
role_confirmed_at,
|
||||
redeemed_at,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
input.orderId,
|
||||
input.orderItemId,
|
||||
input.platformOrderId,
|
||||
input.taskNo,
|
||||
input.taskStatus,
|
||||
input.loginType,
|
||||
input.claimTokenId,
|
||||
input.reservedCdkId,
|
||||
input.browserSessionId,
|
||||
input.nickname,
|
||||
input.roleId,
|
||||
input.roleName,
|
||||
input.area,
|
||||
input.partitionName,
|
||||
input.resultCode,
|
||||
input.resultMessage,
|
||||
input.screenshotPath,
|
||||
input.artifactsJson,
|
||||
input.lastError,
|
||||
input.retryCount,
|
||||
input.expiresAt,
|
||||
input.claimedAt,
|
||||
input.roleConfirmedAt,
|
||||
input.redeemedAt,
|
||||
input.createdAt,
|
||||
input.updatedAt,
|
||||
export async function listTasksByOrderId(orderId) {
|
||||
const result = await query(
|
||||
`${buildTaskSelect()}
|
||||
WHERE ft.order_id = $1
|
||||
ORDER BY ft.id ASC`,
|
||||
[Number(orderId)],
|
||||
)
|
||||
|
||||
return getTaskById(Number(result.lastInsertRowid))
|
||||
return result.rows
|
||||
}
|
||||
|
||||
export function updateTask(taskId, patch) {
|
||||
const current = getTaskById(taskId)
|
||||
export async function createTask(input) {
|
||||
return withTransaction(async (client) => {
|
||||
const taskResult = await client.query(
|
||||
`
|
||||
INSERT INTO fulfillment_tasks (
|
||||
order_id,
|
||||
order_item_id,
|
||||
unit_index,
|
||||
task_no,
|
||||
provider,
|
||||
platform,
|
||||
shop_id,
|
||||
shop_name,
|
||||
platform_order_id,
|
||||
profile_id,
|
||||
executor_key,
|
||||
task_status,
|
||||
inventory_status,
|
||||
delivery_status,
|
||||
result_code,
|
||||
result_message,
|
||||
claim_token,
|
||||
claim_expires_at,
|
||||
automation_mode,
|
||||
requires_claim,
|
||||
user_action_status,
|
||||
attempt_count,
|
||||
last_error,
|
||||
context_json,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10,
|
||||
$11, $12, $13, $14, $15, $16, $17, $18, $19,
|
||||
$20, $21, $22, $23, $24::jsonb, $25, $26
|
||||
)
|
||||
RETURNING id
|
||||
`,
|
||||
[
|
||||
input.orderId,
|
||||
input.orderItemId,
|
||||
input.unitIndex,
|
||||
input.taskNo,
|
||||
input.provider,
|
||||
input.platform,
|
||||
input.shopId || '',
|
||||
input.shopName || '',
|
||||
input.platformOrderId,
|
||||
input.profileId,
|
||||
input.executorKey,
|
||||
input.taskStatus || 'pending_payment',
|
||||
input.inventoryStatus || 'pending',
|
||||
input.deliveryStatus || 'pending',
|
||||
input.resultCode || '',
|
||||
input.resultMessage || '',
|
||||
input.claimToken || '',
|
||||
input.claimExpiresAt || null,
|
||||
input.automationMode || 'manual',
|
||||
Boolean(input.requiresClaim),
|
||||
input.userActionStatus || 'not_required',
|
||||
input.attemptCount || 0,
|
||||
input.lastError || '',
|
||||
input.contextJson || '{}',
|
||||
input.createdAt,
|
||||
input.updatedAt,
|
||||
],
|
||||
)
|
||||
|
||||
const taskId = Number(taskResult.rows[0]?.id || 0)
|
||||
if (input.tencentContext) {
|
||||
await upsertTencentBrowserContextWithClient(client, taskId, input.tencentContext, input.createdAt)
|
||||
}
|
||||
|
||||
return getTaskById(taskId)
|
||||
})
|
||||
}
|
||||
|
||||
export async function updateTask(taskId, patch) {
|
||||
const current = await getTaskById(taskId)
|
||||
if (!current) {
|
||||
return null
|
||||
}
|
||||
|
||||
const next = { ...current, ...patch }
|
||||
return withTransaction(async (client) => {
|
||||
const next = { ...current, ...patch }
|
||||
|
||||
getDb().prepare(`
|
||||
UPDATE delivery_tasks
|
||||
SET
|
||||
task_status = ?,
|
||||
login_type = ?,
|
||||
claim_token_id = ?,
|
||||
reserved_cdk_id = ?,
|
||||
browser_session_id = ?,
|
||||
nickname = ?,
|
||||
role_id = ?,
|
||||
role_name = ?,
|
||||
area = ?,
|
||||
partition_name = ?,
|
||||
result_code = ?,
|
||||
result_message = ?,
|
||||
screenshot_path = ?,
|
||||
artifacts_json = ?,
|
||||
last_error = ?,
|
||||
retry_count = ?,
|
||||
expires_at = ?,
|
||||
claimed_at = ?,
|
||||
role_confirmed_at = ?,
|
||||
redeemed_at = ?,
|
||||
updated_at = ?
|
||||
WHERE id = ?
|
||||
`).run(
|
||||
next.task_status,
|
||||
next.login_type,
|
||||
next.claim_token_id,
|
||||
next.reserved_cdk_id,
|
||||
next.browser_session_id,
|
||||
next.nickname,
|
||||
next.role_id,
|
||||
next.role_name,
|
||||
next.area,
|
||||
next.partition_name,
|
||||
next.result_code,
|
||||
next.result_message,
|
||||
next.screenshot_path,
|
||||
next.artifacts_json,
|
||||
next.last_error,
|
||||
next.retry_count,
|
||||
next.expires_at,
|
||||
next.claimed_at,
|
||||
next.role_confirmed_at,
|
||||
next.redeemed_at,
|
||||
next.updated_at,
|
||||
taskId,
|
||||
await client.query(
|
||||
`
|
||||
UPDATE fulfillment_tasks
|
||||
SET
|
||||
task_status = $1,
|
||||
inventory_status = $2,
|
||||
delivery_status = $3,
|
||||
result_code = $4,
|
||||
result_message = $5,
|
||||
claim_token = $6,
|
||||
claim_expires_at = $7,
|
||||
automation_mode = $8,
|
||||
requires_claim = $9,
|
||||
user_action_status = $10,
|
||||
attempt_count = $11,
|
||||
last_error = $12,
|
||||
context_json = $13::jsonb,
|
||||
updated_at = $14
|
||||
WHERE id = $15
|
||||
`,
|
||||
[
|
||||
next.task_status,
|
||||
next.inventory_status,
|
||||
next.delivery_status,
|
||||
next.result_code,
|
||||
next.result_message,
|
||||
next.claim_token || '',
|
||||
next.claim_expires_at || null,
|
||||
next.automation_mode || 'manual',
|
||||
Boolean(next.requires_claim),
|
||||
next.user_action_status || 'not_required',
|
||||
next.attempt_count || 0,
|
||||
next.last_error || '',
|
||||
next.context_json || '{}',
|
||||
next.updated_at,
|
||||
Number(taskId),
|
||||
],
|
||||
)
|
||||
|
||||
if (containsTencentPatch(patch)) {
|
||||
await upsertTencentBrowserContextWithClient(client, Number(taskId), {
|
||||
browserSessionId: patch.browser_session_id,
|
||||
loginType: patch.login_type,
|
||||
nickname: patch.nickname,
|
||||
roleId: patch.role_id,
|
||||
roleName: patch.role_name,
|
||||
area: patch.area,
|
||||
partitionName: patch.partition_name,
|
||||
screenshotPath: patch.screenshot_path,
|
||||
artifactsJson: patch.artifacts_json,
|
||||
stateJson: patch.state_json,
|
||||
}, patch.updated_at || current.updated_at)
|
||||
}
|
||||
|
||||
return getTaskById(taskId)
|
||||
})
|
||||
}
|
||||
|
||||
export async function getTaskById(taskId) {
|
||||
const result = await query(
|
||||
`${buildTaskSelect()}
|
||||
WHERE ft.id = $1
|
||||
LIMIT 1`,
|
||||
[Number(taskId)],
|
||||
)
|
||||
|
||||
return getTaskById(taskId)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function getTaskById(taskId) {
|
||||
return getDb().prepare('SELECT * FROM delivery_tasks WHERE id = ? LIMIT 1').get(taskId) || null
|
||||
export async function findTaskByClaimTokenId(claimTokenId) {
|
||||
const result = await query(
|
||||
`${buildTaskSelect()}
|
||||
JOIN claim_tokens ctf ON ctf.task_id = ft.id
|
||||
WHERE ctf.id = $1
|
||||
LIMIT 1`,
|
||||
[Number(claimTokenId)],
|
||||
)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function findTaskByClaimTokenId(claimTokenId) {
|
||||
return getDb().prepare(`
|
||||
SELECT *
|
||||
FROM delivery_tasks
|
||||
WHERE claim_token_id = ?
|
||||
LIMIT 1
|
||||
`).get(claimTokenId) || null
|
||||
}
|
||||
|
||||
export function listTasks({
|
||||
export async function listTasks({
|
||||
page = 1,
|
||||
pageSize = 20,
|
||||
status = '',
|
||||
@@ -162,67 +237,173 @@ export function listTasks({
|
||||
const params = []
|
||||
|
||||
if (status) {
|
||||
filters.push('dt.task_status = ?')
|
||||
params.push(status)
|
||||
filters.push(`ft.task_status = $${params.length}`)
|
||||
}
|
||||
|
||||
if (platformOrderId) {
|
||||
filters.push('dt.platform_order_id LIKE ?')
|
||||
params.push(`%${platformOrderId}%`)
|
||||
filters.push(`ft.platform_order_id ILIKE $${params.length}`)
|
||||
}
|
||||
|
||||
if (taskNo) {
|
||||
filters.push('dt.task_no LIKE ?')
|
||||
params.push(`%${taskNo}%`)
|
||||
filters.push(`ft.task_no ILIKE $${params.length}`)
|
||||
}
|
||||
|
||||
if (skuCode) {
|
||||
filters.push('oi.sku_code = ?')
|
||||
params.push(skuCode)
|
||||
filters.push(`oi.sku_code = $${params.length}`)
|
||||
}
|
||||
|
||||
if (roleId) {
|
||||
filters.push('dt.role_id LIKE ?')
|
||||
params.push(`%${roleId}%`)
|
||||
filters.push(`ctx.role_id ILIKE $${params.length}`)
|
||||
}
|
||||
|
||||
if (dateFrom) {
|
||||
filters.push('dt.created_at >= ?')
|
||||
params.push(dateFrom)
|
||||
filters.push(`ft.created_at >= $${params.length}`)
|
||||
}
|
||||
|
||||
if (dateTo) {
|
||||
filters.push('dt.created_at <= ?')
|
||||
params.push(dateTo)
|
||||
filters.push(`ft.created_at <= $${params.length}`)
|
||||
}
|
||||
|
||||
const whereClause = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : ''
|
||||
const db = getDb()
|
||||
const totalRow = db.prepare(`
|
||||
SELECT COUNT(*) AS total
|
||||
FROM delivery_tasks dt
|
||||
LEFT JOIN order_items oi ON oi.id = dt.order_item_id
|
||||
${whereClause}
|
||||
`).get(...params)
|
||||
const totalResult = await query(
|
||||
`
|
||||
SELECT COUNT(*)::int AS total
|
||||
FROM fulfillment_tasks ft
|
||||
LEFT JOIN order_items oi ON oi.id = ft.order_item_id
|
||||
LEFT JOIN tencent_browser_contexts ctx ON ctx.task_id = ft.id
|
||||
${whereClause}
|
||||
`,
|
||||
params,
|
||||
)
|
||||
|
||||
const items = db.prepare(`
|
||||
SELECT
|
||||
dt.*,
|
||||
oi.sku_code,
|
||||
oi.sku_name,
|
||||
ci.cdk_code,
|
||||
ct.token AS claim_token
|
||||
FROM delivery_tasks dt
|
||||
LEFT JOIN order_items oi ON oi.id = dt.order_item_id
|
||||
LEFT JOIN cdk_inventory ci ON ci.id = dt.reserved_cdk_id
|
||||
LEFT JOIN claim_tokens ct ON ct.id = dt.claim_token_id
|
||||
${whereClause}
|
||||
ORDER BY dt.id DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`).all(...params, pageSize, offset)
|
||||
params.push(pageSize)
|
||||
params.push(offset)
|
||||
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}
|
||||
ORDER BY ft.id DESC
|
||||
LIMIT $${params.length - 1} OFFSET $${params.length}`,
|
||||
params,
|
||||
)
|
||||
|
||||
return {
|
||||
items,
|
||||
total: Number(totalRow?.total || 0),
|
||||
items: itemsResult.rows,
|
||||
total: Number(totalResult.rows[0]?.total || 0),
|
||||
}
|
||||
}
|
||||
|
||||
async function upsertTencentBrowserContextWithClient(client, taskId, patch = {}, timestamp) {
|
||||
const currentResult = await client.query(
|
||||
'SELECT * FROM tencent_browser_contexts WHERE task_id = $1 LIMIT 1',
|
||||
[Number(taskId)],
|
||||
)
|
||||
const current = currentResult.rows[0] || null
|
||||
|
||||
const next = {
|
||||
browser_session_id: patch.browserSessionId ?? current?.browser_session_id ?? '',
|
||||
login_type: patch.loginType ?? current?.login_type ?? '',
|
||||
nickname: patch.nickname ?? current?.nickname ?? '',
|
||||
role_id: patch.roleId ?? current?.role_id ?? '',
|
||||
role_name: patch.roleName ?? current?.role_name ?? '',
|
||||
area: patch.area ?? current?.area ?? '',
|
||||
partition_name: patch.partitionName ?? current?.partition_name ?? '',
|
||||
screenshot_path: patch.screenshotPath ?? current?.screenshot_path ?? '',
|
||||
artifacts_json: patch.artifactsJson ?? current?.artifacts_json ?? '{}',
|
||||
state_json: patch.stateJson ?? current?.state_json ?? '{}',
|
||||
updated_at: timestamp,
|
||||
}
|
||||
|
||||
if (!current) {
|
||||
await client.query(
|
||||
`
|
||||
INSERT INTO tencent_browser_contexts (
|
||||
task_id,
|
||||
browser_session_id,
|
||||
login_type,
|
||||
nickname,
|
||||
role_id,
|
||||
role_name,
|
||||
area,
|
||||
partition_name,
|
||||
screenshot_path,
|
||||
artifacts_json,
|
||||
state_json,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::jsonb, $11::jsonb, $12, $13)
|
||||
`,
|
||||
[
|
||||
Number(taskId),
|
||||
next.browser_session_id,
|
||||
next.login_type,
|
||||
next.nickname,
|
||||
next.role_id,
|
||||
next.role_name,
|
||||
next.area,
|
||||
next.partition_name,
|
||||
next.screenshot_path,
|
||||
typeof next.artifacts_json === 'string' ? next.artifacts_json : JSON.stringify(next.artifacts_json || {}),
|
||||
typeof next.state_json === 'string' ? next.state_json : JSON.stringify(next.state_json || {}),
|
||||
timestamp,
|
||||
timestamp,
|
||||
],
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
await client.query(
|
||||
`
|
||||
UPDATE tencent_browser_contexts
|
||||
SET
|
||||
browser_session_id = $1,
|
||||
login_type = $2,
|
||||
nickname = $3,
|
||||
role_id = $4,
|
||||
role_name = $5,
|
||||
area = $6,
|
||||
partition_name = $7,
|
||||
screenshot_path = $8,
|
||||
artifacts_json = $9::jsonb,
|
||||
state_json = $10::jsonb,
|
||||
updated_at = $11
|
||||
WHERE task_id = $12
|
||||
`,
|
||||
[
|
||||
next.browser_session_id,
|
||||
next.login_type,
|
||||
next.nickname,
|
||||
next.role_id,
|
||||
next.role_name,
|
||||
next.area,
|
||||
next.partition_name,
|
||||
next.screenshot_path,
|
||||
typeof next.artifacts_json === 'string' ? next.artifacts_json : JSON.stringify(next.artifacts_json || {}),
|
||||
typeof next.state_json === 'string' ? next.state_json : JSON.stringify(next.state_json || {}),
|
||||
timestamp,
|
||||
Number(taskId),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
function containsTencentPatch(patch = {}) {
|
||||
return [
|
||||
'browser_session_id',
|
||||
'login_type',
|
||||
'nickname',
|
||||
'role_id',
|
||||
'role_name',
|
||||
'area',
|
||||
'partition_name',
|
||||
'screenshot_path',
|
||||
'artifacts_json',
|
||||
'state_json',
|
||||
].some((key) => Object.prototype.hasOwnProperty.call(patch, key))
|
||||
}
|
||||
|
||||
@@ -1,69 +1,76 @@
|
||||
import { getDb } from '../db/client.js'
|
||||
import { query } from '../db/client.js'
|
||||
|
||||
export function createWebhookEvent(input) {
|
||||
const result = getDb().prepare(`
|
||||
INSERT INTO webhook_events (
|
||||
provider,
|
||||
platform,
|
||||
shop_id,
|
||||
shop_name,
|
||||
event_type,
|
||||
event_key,
|
||||
signature_valid,
|
||||
headers_json,
|
||||
query_json,
|
||||
body_json,
|
||||
processed,
|
||||
process_error,
|
||||
related_order_id,
|
||||
created_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
input.provider,
|
||||
input.platform,
|
||||
input.shopId,
|
||||
input.shopName,
|
||||
input.eventType,
|
||||
input.eventKey,
|
||||
input.signatureValid ? 1 : 0,
|
||||
input.headersJson,
|
||||
input.queryJson,
|
||||
input.bodyJson,
|
||||
input.processed ? 1 : 0,
|
||||
input.processError,
|
||||
input.relatedOrderId,
|
||||
input.createdAt,
|
||||
export async function createWebhookEvent(input) {
|
||||
const result = await query(
|
||||
`
|
||||
INSERT INTO webhook_events (
|
||||
provider,
|
||||
platform,
|
||||
shop_id,
|
||||
shop_name,
|
||||
event_type,
|
||||
event_key,
|
||||
signature_valid,
|
||||
headers_json,
|
||||
query_json,
|
||||
body_json,
|
||||
processed,
|
||||
process_error,
|
||||
related_order_id,
|
||||
created_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9::jsonb, $10::jsonb, $11, $12, $13, $14)
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
input.provider,
|
||||
input.platform,
|
||||
input.shopId || '',
|
||||
input.shopName || '',
|
||||
input.eventType,
|
||||
input.eventKey,
|
||||
Boolean(input.signatureValid),
|
||||
input.headersJson || '{}',
|
||||
input.queryJson || '{}',
|
||||
input.bodyJson || '{}',
|
||||
Boolean(input.processed),
|
||||
input.processError || '',
|
||||
input.relatedOrderId || null,
|
||||
input.createdAt,
|
||||
],
|
||||
)
|
||||
|
||||
return getWebhookEventById(Number(result.lastInsertRowid))
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function updateWebhookEvent(eventId, patch) {
|
||||
const current = getWebhookEventById(eventId)
|
||||
|
||||
export async function updateWebhookEvent(eventId, patch) {
|
||||
const current = await getWebhookEventById(eventId)
|
||||
if (!current) {
|
||||
return null
|
||||
}
|
||||
|
||||
const next = { ...current, ...patch }
|
||||
const result = await query(
|
||||
`
|
||||
UPDATE webhook_events
|
||||
SET
|
||||
processed = $1,
|
||||
process_error = $2,
|
||||
related_order_id = $3
|
||||
WHERE id = $4
|
||||
RETURNING *
|
||||
`,
|
||||
[Boolean(next.processed), next.process_error || '', next.related_order_id || null, Number(eventId)],
|
||||
)
|
||||
|
||||
getDb().prepare(`
|
||||
UPDATE webhook_events
|
||||
SET
|
||||
processed = ?,
|
||||
process_error = ?,
|
||||
related_order_id = ?
|
||||
WHERE id = ?
|
||||
`).run(next.processed ? 1 : 0, next.process_error, next.related_order_id, eventId)
|
||||
|
||||
return getWebhookEventById(eventId)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function getWebhookEventById(eventId) {
|
||||
return getDb().prepare('SELECT * FROM webhook_events WHERE id = ? LIMIT 1').get(eventId) || null
|
||||
export async function getWebhookEventById(eventId) {
|
||||
const result = await query('SELECT * FROM webhook_events WHERE id = $1 LIMIT 1', [Number(eventId)])
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export function listWebhookEvents({
|
||||
export async function listWebhookEvents({
|
||||
page = 1,
|
||||
pageSize = 20,
|
||||
provider = '',
|
||||
@@ -78,62 +85,67 @@ export function listWebhookEvents({
|
||||
const params = []
|
||||
|
||||
if (provider) {
|
||||
filters.push('provider = ?')
|
||||
params.push(provider)
|
||||
filters.push(`provider = $${params.length}`)
|
||||
}
|
||||
|
||||
if (platform) {
|
||||
filters.push('platform = ?')
|
||||
params.push(platform)
|
||||
filters.push(`platform = $${params.length}`)
|
||||
}
|
||||
|
||||
if (processed === '0' || processed === '1') {
|
||||
filters.push('processed = ?')
|
||||
params.push(Number(processed))
|
||||
params.push(processed === '1')
|
||||
filters.push(`processed = $${params.length}`)
|
||||
}
|
||||
|
||||
if (relatedOrderId) {
|
||||
filters.push('related_order_id = ?')
|
||||
params.push(Number(relatedOrderId))
|
||||
filters.push(`related_order_id = $${params.length}`)
|
||||
}
|
||||
|
||||
if (dateFrom) {
|
||||
filters.push('created_at >= ?')
|
||||
params.push(dateFrom)
|
||||
filters.push(`created_at >= $${params.length}`)
|
||||
}
|
||||
|
||||
if (dateTo) {
|
||||
filters.push('created_at <= ?')
|
||||
params.push(dateTo)
|
||||
filters.push(`created_at <= $${params.length}`)
|
||||
}
|
||||
|
||||
const whereClause = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : ''
|
||||
const db = getDb()
|
||||
const totalRow = db.prepare(`
|
||||
SELECT COUNT(*) AS total
|
||||
FROM webhook_events
|
||||
${whereClause}
|
||||
`).get(...params)
|
||||
const totalResult = await query(`SELECT COUNT(*)::int AS total FROM webhook_events ${whereClause}`, params)
|
||||
|
||||
const items = db.prepare(`
|
||||
SELECT *
|
||||
FROM webhook_events
|
||||
${whereClause}
|
||||
ORDER BY id DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`).all(...params, pageSize, offset)
|
||||
params.push(pageSize)
|
||||
params.push(offset)
|
||||
const itemsResult = await query(
|
||||
`
|
||||
SELECT *
|
||||
FROM webhook_events
|
||||
${whereClause}
|
||||
ORDER BY id DESC
|
||||
LIMIT $${params.length - 1} OFFSET $${params.length}
|
||||
`,
|
||||
params,
|
||||
)
|
||||
|
||||
return {
|
||||
items,
|
||||
total: Number(totalRow?.total || 0),
|
||||
items: itemsResult.rows,
|
||||
total: Number(totalResult.rows[0]?.total || 0),
|
||||
}
|
||||
}
|
||||
|
||||
export function listWebhookEventsByOrderId(orderId) {
|
||||
return getDb().prepare(`
|
||||
SELECT *
|
||||
FROM webhook_events
|
||||
WHERE related_order_id = ?
|
||||
ORDER BY id DESC
|
||||
`).all(orderId)
|
||||
export async function listWebhookEventsByOrderId(orderId) {
|
||||
const result = await query(
|
||||
`
|
||||
SELECT *
|
||||
FROM webhook_events
|
||||
WHERE related_order_id = $1
|
||||
ORDER BY id DESC
|
||||
`,
|
||||
[Number(orderId)],
|
||||
)
|
||||
|
||||
return result.rows
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user