抽离后台履约观察商品查询逻辑
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,
|
normalizeFulfillmentLookupPayload,
|
||||||
resolveFulfillmentLookupDetail,
|
resolveFulfillmentLookupDetail,
|
||||||
} from './admin-platform-config-fulfillment.js'
|
} from './admin-platform-config-fulfillment.js'
|
||||||
|
import { listAdminObservedProducts } from './admin-platform-config-observed-products.js'
|
||||||
import {
|
import {
|
||||||
assertAdminFulfillmentBindingsInput,
|
assertAdminFulfillmentBindingsInput,
|
||||||
buildAdminFulfillmentBindingUniqueKey,
|
buildAdminFulfillmentBindingUniqueKey,
|
||||||
@@ -99,7 +100,6 @@ import {
|
|||||||
mapAdminAgisoMessagingDefaults,
|
mapAdminAgisoMessagingDefaults,
|
||||||
mapAdminAgisoShopConfigItem,
|
mapAdminAgisoShopConfigItem,
|
||||||
mapAdminFulfillmentBindingConfigItem,
|
mapAdminFulfillmentBindingConfigItem,
|
||||||
mapAdminObservedProductItem,
|
|
||||||
} from './admin-platform-config-domain.js'
|
} from './admin-platform-config-domain.js'
|
||||||
import {
|
import {
|
||||||
mapAdminCloudtentaclesSession,
|
mapAdminCloudtentaclesSession,
|
||||||
@@ -721,50 +721,11 @@ export async function runAdminCloudtentaclesFullFlow(payload = /** @type {AdminC
|
|||||||
|
|
||||||
export async function getAdminFulfillmentBindingConfigs() {
|
export async function getAdminFulfillmentBindingConfigs() {
|
||||||
const bindings = getOrderFulfillmentBindingConfigs()
|
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 {
|
return {
|
||||||
filePath: getOrderFulfillmentBindingsFilePath(),
|
filePath: getOrderFulfillmentBindingsFilePath(),
|
||||||
bindings: bindings.map(mapAdminFulfillmentBindingConfigItem),
|
bindings: bindings.map(mapAdminFulfillmentBindingConfigItem),
|
||||||
observedProducts: rowsResult.rows.map((row) => mapAdminObservedProductItem({
|
observedProducts: await listAdminObservedProducts(bindings),
|
||||||
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)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user