抽离后台履约观察商品查询逻辑
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
// @ts-check
|
||||
|
||||
import { query } from '../../db/client.js'
|
||||
import { mapAdminObservedProductItem } from './admin-platform-config-domain.js'
|
||||
|
||||
export const ADMIN_OBSERVED_PRODUCTS_QUERY = `
|
||||
SELECT
|
||||
o.provider,
|
||||
o.platform,
|
||||
o.shop_id,
|
||||
MAX(CASE WHEN trim(o.shop_name) != '' THEN o.shop_name ELSE '' END) AS shop_name,
|
||||
COALESCE(NULLIF(oi.item_snapshot_json ->> 'externalItemId', ''), '') AS external_item_id,
|
||||
COALESCE(NULLIF(oi.item_snapshot_json ->> 'externalSkuCode', ''), '') AS external_sku_code,
|
||||
COALESCE(NULLIF(oi.item_snapshot_json ->> 'externalSkuName', ''), '') AS external_sku_name,
|
||||
MAX(oi.created_at) AS latest_seen_at,
|
||||
COUNT(*)::int AS order_item_count
|
||||
FROM order_items oi
|
||||
JOIN orders o ON o.id = oi.order_id
|
||||
WHERE
|
||||
COALESCE(NULLIF(oi.item_snapshot_json ->> 'externalItemId', ''), '') != ''
|
||||
OR COALESCE(NULLIF(oi.item_snapshot_json ->> 'externalSkuCode', ''), '') != ''
|
||||
OR COALESCE(NULLIF(oi.item_snapshot_json ->> 'externalSkuName', ''), '') != ''
|
||||
GROUP BY
|
||||
o.provider,
|
||||
o.platform,
|
||||
o.shop_id,
|
||||
external_item_id,
|
||||
external_sku_code,
|
||||
external_sku_name
|
||||
ORDER BY latest_seen_at DESC, o.platform ASC, o.shop_id ASC, external_sku_code ASC
|
||||
LIMIT 200
|
||||
`
|
||||
|
||||
export function mapAdminObservedProductRow(row = {}) {
|
||||
return {
|
||||
provider: String(row.provider || '').trim(),
|
||||
platform: String(row.platform || '').trim(),
|
||||
shopId: String(row.shop_id || '').trim(),
|
||||
shopName: String(row.shop_name || '').trim(),
|
||||
externalItemId: String(row.external_item_id || '').trim(),
|
||||
externalSkuCode: String(row.external_sku_code || '').trim(),
|
||||
externalSkuName: String(row.external_sku_name || '').trim(),
|
||||
latestSeenAt: row.latest_seen_at || null,
|
||||
orderItemCount: Number(row.order_item_count || 0),
|
||||
}
|
||||
}
|
||||
|
||||
export async function listAdminObservedProducts(bindings = [], options = {}) {
|
||||
const queryImpl = /** @type {(sql: string) => Promise<{ rows: Array<Record<string, unknown>> }>} */ (
|
||||
options.query || query
|
||||
)
|
||||
const rowsResult = await queryImpl(ADMIN_OBSERVED_PRODUCTS_QUERY)
|
||||
return rowsResult.rows.map((row) => mapAdminObservedProductItem(mapAdminObservedProductRow(row), bindings))
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
import {
|
||||
ADMIN_OBSERVED_PRODUCTS_QUERY,
|
||||
listAdminObservedProducts,
|
||||
mapAdminObservedProductRow,
|
||||
} from './admin-platform-config-observed-products.js'
|
||||
|
||||
test('mapAdminObservedProductRow normalizes query row fields', () => {
|
||||
assert.deepEqual(
|
||||
mapAdminObservedProductRow({
|
||||
provider: ' agiso ',
|
||||
platform: ' xianyu ',
|
||||
shop_id: ' shop-1 ',
|
||||
shop_name: ' 店铺A ',
|
||||
external_item_id: ' item-1 ',
|
||||
external_sku_code: ' sku-1 ',
|
||||
external_sku_name: ' 商品A ',
|
||||
latest_seen_at: '2026-05-04T12:00:00.000Z',
|
||||
order_item_count: '3',
|
||||
}),
|
||||
{
|
||||
provider: 'agiso',
|
||||
platform: 'xianyu',
|
||||
shopId: 'shop-1',
|
||||
shopName: '店铺A',
|
||||
externalItemId: 'item-1',
|
||||
externalSkuCode: 'sku-1',
|
||||
externalSkuName: '商品A',
|
||||
latestSeenAt: '2026-05-04T12:00:00.000Z',
|
||||
orderItemCount: 3,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
test('listAdminObservedProducts queries rows and maps matched binding summary', async () => {
|
||||
/** @type {string[]} */
|
||||
const sqlCalls = []
|
||||
const result = await listAdminObservedProducts(
|
||||
[
|
||||
{
|
||||
provider: 'agiso',
|
||||
platform: 'xianyu',
|
||||
shopId: 'shop-1',
|
||||
skuCode: 'inner-1',
|
||||
skuName: '内部商品A',
|
||||
profileKey: 'manual_review',
|
||||
match: {
|
||||
externalSkuCode: 'sku-1',
|
||||
},
|
||||
},
|
||||
],
|
||||
{
|
||||
async query(sql) {
|
||||
sqlCalls.push(sql)
|
||||
return {
|
||||
rows: [
|
||||
{
|
||||
provider: 'agiso',
|
||||
platform: 'xianyu',
|
||||
shop_id: 'shop-1',
|
||||
shop_name: '店铺A',
|
||||
external_item_id: '',
|
||||
external_sku_code: 'sku-1',
|
||||
external_sku_name: '商品A',
|
||||
latest_seen_at: '2026-05-04T12:00:00.000Z',
|
||||
order_item_count: 2,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
assert.equal(sqlCalls.length, 1)
|
||||
assert.equal(sqlCalls[0], ADMIN_OBSERVED_PRODUCTS_QUERY)
|
||||
assert.deepEqual(result, [
|
||||
{
|
||||
provider: 'agiso',
|
||||
platform: 'xianyu',
|
||||
shopId: 'shop-1',
|
||||
shopName: '店铺A',
|
||||
externalItemId: '',
|
||||
externalSkuCode: 'sku-1',
|
||||
externalSkuName: '商品A',
|
||||
latestSeenAt: '2026-05-04T12:00:00.000Z',
|
||||
orderItemCount: 2,
|
||||
configured: true,
|
||||
matchedBinding: {
|
||||
skuCode: 'inner-1',
|
||||
skuName: '内部商品A',
|
||||
profileKey: 'manual_review',
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
@@ -89,6 +89,7 @@ import {
|
||||
normalizeFulfillmentLookupPayload,
|
||||
resolveFulfillmentLookupDetail,
|
||||
} from './admin-platform-config-fulfillment.js'
|
||||
import { listAdminObservedProducts } from './admin-platform-config-observed-products.js'
|
||||
import {
|
||||
assertAdminFulfillmentBindingsInput,
|
||||
buildAdminFulfillmentBindingUniqueKey,
|
||||
@@ -99,7 +100,6 @@ import {
|
||||
mapAdminAgisoMessagingDefaults,
|
||||
mapAdminAgisoShopConfigItem,
|
||||
mapAdminFulfillmentBindingConfigItem,
|
||||
mapAdminObservedProductItem,
|
||||
} from './admin-platform-config-domain.js'
|
||||
import {
|
||||
mapAdminCloudtentaclesSession,
|
||||
@@ -721,50 +721,11 @@ export async function runAdminCloudtentaclesFullFlow(payload = /** @type {AdminC
|
||||
|
||||
export async function getAdminFulfillmentBindingConfigs() {
|
||||
const bindings = getOrderFulfillmentBindingConfigs()
|
||||
const rowsResult = await query(
|
||||
`
|
||||
SELECT
|
||||
o.provider,
|
||||
o.platform,
|
||||
o.shop_id,
|
||||
MAX(CASE WHEN trim(o.shop_name) != '' THEN o.shop_name ELSE '' END) AS shop_name,
|
||||
COALESCE(NULLIF(oi.item_snapshot_json ->> 'externalItemId', ''), '') AS external_item_id,
|
||||
COALESCE(NULLIF(oi.item_snapshot_json ->> 'externalSkuCode', ''), '') AS external_sku_code,
|
||||
COALESCE(NULLIF(oi.item_snapshot_json ->> 'externalSkuName', ''), '') AS external_sku_name,
|
||||
MAX(oi.created_at) AS latest_seen_at,
|
||||
COUNT(*)::int AS order_item_count
|
||||
FROM order_items oi
|
||||
JOIN orders o ON o.id = oi.order_id
|
||||
WHERE
|
||||
COALESCE(NULLIF(oi.item_snapshot_json ->> 'externalItemId', ''), '') != ''
|
||||
OR COALESCE(NULLIF(oi.item_snapshot_json ->> 'externalSkuCode', ''), '') != ''
|
||||
OR COALESCE(NULLIF(oi.item_snapshot_json ->> 'externalSkuName', ''), '') != ''
|
||||
GROUP BY
|
||||
o.provider,
|
||||
o.platform,
|
||||
o.shop_id,
|
||||
external_item_id,
|
||||
external_sku_code,
|
||||
external_sku_name
|
||||
ORDER BY latest_seen_at DESC, o.platform ASC, o.shop_id ASC, external_sku_code ASC
|
||||
LIMIT 200
|
||||
`,
|
||||
)
|
||||
|
||||
return {
|
||||
filePath: getOrderFulfillmentBindingsFilePath(),
|
||||
bindings: bindings.map(mapAdminFulfillmentBindingConfigItem),
|
||||
observedProducts: rowsResult.rows.map((row) => mapAdminObservedProductItem({
|
||||
provider: String(row.provider || '').trim(),
|
||||
platform: String(row.platform || '').trim(),
|
||||
shopId: String(row.shop_id || '').trim(),
|
||||
shopName: String(row.shop_name || '').trim(),
|
||||
externalItemId: String(row.external_item_id || '').trim(),
|
||||
externalSkuCode: String(row.external_sku_code || '').trim(),
|
||||
externalSkuName: String(row.external_sku_name || '').trim(),
|
||||
latestSeenAt: row.latest_seen_at || null,
|
||||
orderItemCount: Number(row.order_item_count || 0),
|
||||
}, bindings)),
|
||||
observedProducts: await listAdminObservedProducts(bindings),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user