perf(finance): 消除定时同步与后台页面的重查询
- 新增 order_items.manual_sku_key 生成列(迁移 106),统一内部 SKU 口径(skuNick → outSkuSn → sku_code → sku_name,去空格) - 人工渠道匹配 8.4s → 62ms;人工商品归一写回 7.0s → 307ms - 销售事实重算改为物化订单集合 + 预聚合成本/退款,6.9s → 0.5s 内 - 成本异常写入去掉逐项 LATERAL 工单探测,收口按最近 7 天窗口,7–8s → 0.5s 内 - 销售毛利订单统计去掉重复 NOT EXISTS,9.4s → 0.4s - 接单平台店铺下拉改 EXISTS 半连接,3.1s → 0.3s - cost-finance-sync 默认间隔 5 分钟 → 15 分钟;完整同步 51.2s → 34.9s
This commit is contained in:
@@ -57,13 +57,9 @@ export async function syncEstimatedOrderItemCostSnapshots(
|
||||
COALESCE(oi.fulfillment_mode, 'manual') AS fulfillment_mode
|
||||
FROM order_items oi
|
||||
JOIN orders o ON o.id = oi.order_id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT id, product_rule_id
|
||||
FROM work_orders
|
||||
WHERE order_item_id = oi.id
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
) wo ON TRUE
|
||||
-- order_item_id 上有唯一部分索引,一个订单项最多一张工单;
|
||||
-- 原来的 ORDER BY id DESC LIMIT 1 逐行探测会做 3.7 万次索引查找。
|
||||
LEFT JOIN work_orders wo ON wo.order_item_id = oi.id
|
||||
WHERE o.pay_status IN ('paid', 'refunded')
|
||||
AND (COALESCE(o.paid_at, o.created_at) AT TIME ZONE 'Asia/Shanghai')::date
|
||||
BETWEEN $1::date AND $2::date
|
||||
@@ -613,9 +609,7 @@ async function insertAndResolveProductMappingExceptions(
|
||||
COALESCE(oi.fulfillment_mode, 'manual') AS fulfillment_mode
|
||||
FROM order_items oi
|
||||
JOIN orders o ON o.id = oi.order_id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT id FROM work_orders WHERE order_item_id = oi.id ORDER BY id DESC LIMIT 1
|
||||
) wo ON TRUE
|
||||
LEFT JOIN work_orders wo ON wo.order_item_id = oi.id
|
||||
WHERE o.pay_status IN ('paid', 'refunded')
|
||||
AND (COALESCE(o.paid_at, o.created_at) AT TIME ZONE 'Asia/Shanghai')::date
|
||||
BETWEEN $1::date AND $2::date
|
||||
@@ -651,11 +645,15 @@ async function insertAndResolveProductMappingExceptions(
|
||||
WHERE ce.status = 'open'
|
||||
AND ce.subject_type = 'order_item'
|
||||
AND ce.reason = 'product_mapping_missing'
|
||||
-- 只收口最近创建、仍在同步视野内的异常:全表收口会随着历史异常累积
|
||||
-- 变成每次同步都扫几十万行(41 万单测试库实测单次 7.4 秒)。
|
||||
AND ce.created_at >= ($1::date - INTERVAL '7 days')
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM order_items oi
|
||||
WHERE oi.id = ce.subject_id AND oi.canonical_product_id IS NOT NULL
|
||||
)
|
||||
`,
|
||||
[fromDate],
|
||||
)
|
||||
|
||||
return { created: inserted.rowCount || 0, resolved: resolved.rowCount || 0 }
|
||||
@@ -678,9 +676,7 @@ async function insertAndResolveCostMissingExceptions(
|
||||
COALESCE(oi.fulfillment_mode, 'manual') AS fulfillment_mode
|
||||
FROM order_items oi
|
||||
JOIN orders o ON o.id = oi.order_id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT id FROM work_orders WHERE order_item_id = oi.id ORDER BY id DESC LIMIT 1
|
||||
) wo ON TRUE
|
||||
LEFT JOIN work_orders wo ON wo.order_item_id = oi.id
|
||||
WHERE o.pay_status IN ('paid', 'refunded')
|
||||
AND (COALESCE(o.paid_at, o.created_at) AT TIME ZONE 'Asia/Shanghai')::date
|
||||
BETWEEN $1::date AND $2::date
|
||||
@@ -774,6 +770,7 @@ async function insertAndResolveCostMissingExceptions(
|
||||
WHERE ce.status = 'open'
|
||||
AND ce.subject_type = 'order_item'
|
||||
AND ce.reason = 'cost_missing'
|
||||
AND ce.created_at >= ($1::date - INTERVAL '7 days')
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM cost_snapshots cs
|
||||
WHERE cs.subject_type = 'order_item'
|
||||
@@ -781,6 +778,7 @@ async function insertAndResolveCostMissingExceptions(
|
||||
AND cs.superseded_at IS NULL
|
||||
)
|
||||
`,
|
||||
[fromDate],
|
||||
)
|
||||
|
||||
const manualResolved = await client.query(
|
||||
@@ -790,6 +788,7 @@ async function insertAndResolveCostMissingExceptions(
|
||||
WHERE ce.status = 'open'
|
||||
AND ce.subject_type = 'order_item'
|
||||
AND ce.reason = 'manual_cost_missing'
|
||||
AND ce.created_at >= ($1::date - INTERVAL '7 days')
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM cost_snapshots cs
|
||||
WHERE cs.subject_type = 'manual_case'
|
||||
@@ -797,6 +796,7 @@ async function insertAndResolveCostMissingExceptions(
|
||||
AND cs.superseded_at IS NULL
|
||||
)
|
||||
`,
|
||||
[fromDate],
|
||||
)
|
||||
|
||||
return {
|
||||
|
||||
@@ -124,12 +124,7 @@ export async function scanManualSkus(options: ScanOptions) {
|
||||
oi.sku_name,
|
||||
COALESCE(oi.revenue_amount_fen, 0)::bigint AS revenue_fen,
|
||||
(COALESCE(o.paid_at, o.created_at) AT TIME ZONE 'Asia/Shanghai')::date AS stat_date,
|
||||
COALESCE(
|
||||
NULLIF(oi.spec_json->>'skuNick', ''),
|
||||
NULLIF(oi.spec_json->>'outSkuSn', ''),
|
||||
NULLIF(oi.sku_code, ''),
|
||||
NULLIF(oi.sku_name, '')
|
||||
) AS sku_key
|
||||
oi.manual_sku_key AS sku_key
|
||||
FROM order_items oi
|
||||
JOIN orders o ON o.id = oi.order_id
|
||||
WHERE oi.fulfillment_mode = 'manual'
|
||||
@@ -247,7 +242,7 @@ export async function listManualSkus(input: Record<string, unknown> = {}) {
|
||||
JOIN orders o ON o.id = oi.order_id
|
||||
WHERE oi.fulfillment_mode = 'manual'
|
||||
AND o.provider = $${params.length}
|
||||
AND COALESCE(NULLIF(oi.spec_json->>'skuNick', ''), NULLIF(oi.spec_json->>'outSkuSn', '')) = r.sku_key
|
||||
AND oi.manual_sku_key = r.sku_key
|
||||
)`)
|
||||
}
|
||||
|
||||
@@ -435,12 +430,7 @@ export async function rematchManualFulfillments(input: Record<string, unknown> =
|
||||
FROM order_items oi
|
||||
JOIN orders o ON o.id = oi.order_id
|
||||
JOIN manual_fulfillment_configs cfg
|
||||
ON cfg.sku_key = COALESCE(
|
||||
NULLIF(oi.spec_json->>'skuNick', ''),
|
||||
NULLIF(oi.spec_json->>'outSkuSn', ''),
|
||||
NULLIF(oi.sku_code, ''),
|
||||
NULLIF(oi.sku_name, '')
|
||||
)
|
||||
ON cfg.sku_key = oi.manual_sku_key
|
||||
WHERE oi.fulfillment_mode = 'manual'
|
||||
AND cfg.enabled = TRUE
|
||||
AND ($1 = '' OR cfg.sku_key = $1)
|
||||
|
||||
@@ -34,14 +34,11 @@ export type NormalizeManualProductsResult = {
|
||||
remainingItemCount: number
|
||||
}
|
||||
|
||||
/** 内部 SKU:快手 skuNick / 拼多多 outSkuSn / 平台 sku_code 兜底。 */
|
||||
const MANUAL_SKU_KEY_SQL = `
|
||||
COALESCE(
|
||||
NULLIF(TRIM(oi.spec_json->>'skuNick'), ''),
|
||||
NULLIF(TRIM(oi.spec_json->>'outSkuSn'), ''),
|
||||
NULLIF(TRIM(oi.sku_code), '')
|
||||
)
|
||||
`
|
||||
/**
|
||||
* 内部 SKU:由 order_items.manual_sku_key 生成列统一提供
|
||||
* (skuNick → outSkuSn → sku_code → sku_name,均去空格)。
|
||||
*/
|
||||
const MANUAL_SKU_KEY_SQL = `oi.manual_sku_key`
|
||||
|
||||
export async function normalizeManualProducts(
|
||||
input: NormalizeManualProductsInput = {},
|
||||
|
||||
Reference in New Issue
Block a user