docs(finance): 履约与财务分流统一方案与只读审计脚本

This commit is contained in:
yml2213
2026-11-02 13:48:18 +08:00
parent 8da669b1fd
commit 652d501c2c
2 changed files with 379 additions and 0 deletions
@@ -0,0 +1,151 @@
/**
* 履约载体 ↔ 财务渠道 对齐审计(只读)。
*
* 用法:npx tsx scripts/audit-fulfillment-finance-alignment.ts
*/
import { closeDb, query } from '../src/db/client.js'
async function main() {
const pending = await query<{
total: number
with_work_order: number
with_affiliate_task: number
with_manual_task: number
no_carrier: number
}>(
`
SELECT
count(*)::int AS total,
count(*) FILTER (WHERE EXISTS (SELECT 1 FROM work_orders wo WHERE wo.order_item_id = oi.id))::int AS with_work_order,
count(*) FILTER (WHERE EXISTS (SELECT 1 FROM fulfillment_tasks ft WHERE ft.order_item_id = oi.id AND ft.executor_key = 'affiliate_dash'))::int AS with_affiliate_task,
count(*) FILTER (WHERE EXISTS (SELECT 1 FROM fulfillment_tasks ft WHERE ft.order_item_id = oi.id AND ft.executor_key = 'manual_dispatch'))::int AS with_manual_task,
count(*) FILTER (
WHERE NOT EXISTS (SELECT 1 FROM work_orders wo WHERE wo.order_item_id = oi.id)
AND NOT EXISTS (SELECT 1 FROM fulfillment_tasks ft WHERE ft.order_item_id = oi.id)
)::int AS no_carrier
FROM order_items oi
WHERE oi.fulfillment_status = 'pending'
`,
)
const channelCarrier = await query<{
channel: string
total: number
work_orders: number
affiliate_tasks: number
manual_tasks: number
no_carrier: number
}>(
`
SELECT
COALESCE(NULLIF(oi.fulfillment_channel, ''), '(pending)') AS channel,
count(*)::int AS total,
count(*) FILTER (WHERE EXISTS (SELECT 1 FROM work_orders wo WHERE wo.order_item_id = oi.id))::int AS work_orders,
count(*) FILTER (WHERE EXISTS (SELECT 1 FROM fulfillment_tasks ft WHERE ft.order_item_id = oi.id AND ft.executor_key = 'affiliate_dash'))::int AS affiliate_tasks,
count(*) FILTER (WHERE EXISTS (SELECT 1 FROM fulfillment_tasks ft WHERE ft.order_item_id = oi.id AND ft.executor_key = 'manual_dispatch'))::int AS manual_tasks,
count(*) FILTER (
WHERE NOT EXISTS (SELECT 1 FROM work_orders wo WHERE wo.order_item_id = oi.id)
AND NOT EXISTS (SELECT 1 FROM fulfillment_tasks ft WHERE ft.order_item_id = oi.id)
)::int AS no_carrier
FROM order_items oi
WHERE oi.fulfillment_status = 'assigned'
GROUP BY 1
ORDER BY 2 DESC
`,
)
const internalSku = await query<{
total: number
null_or_blank: number
numeric_only: number
with_cjk: number
}>(
`
SELECT
count(*)::int AS total,
count(*) FILTER (WHERE btrim(COALESCE(manual_sku_key, '')) = '')::int AS null_or_blank,
count(*) FILTER (WHERE btrim(COALESCE(manual_sku_key, '')) ~ '^[0-9]+$')::int AS numeric_only,
count(*) FILTER (WHERE btrim(COALESCE(manual_sku_key, '')) ~ '[一-龥]')::int AS with_cjk
FROM order_items
WHERE fulfillment_status = 'assigned'
`,
)
const rules = await query<{
match_key_type: string
channel: string
scope: string
total: number
}>(
`
SELECT match_key_type, channel,
CASE WHEN provider = '' AND shop_id = '' THEN 'global' ELSE 'scoped' END AS scope,
count(*)::int AS total
FROM sku_channel_rules
GROUP BY 1, 2, 3
ORDER BY 1, 2, 3
`,
)
const duplicateProducts = await query<{ duplicate_names: number; duplicate_rows: number }>(
`
SELECT count(*)::int AS duplicate_names, COALESCE(sum(total), 0)::int AS duplicate_rows
FROM (
SELECT canonical_name, count(*) AS total
FROM product_catalog
WHERE status = 'active'
GROUP BY canonical_name
HAVING count(*) > 1
) grouped
`,
)
const capabilities = await query<{ channel: string; total: number }>(
`SELECT channel, count(*)::int AS total FROM product_channel_capabilities WHERE enabled GROUP BY 1 ORDER BY 2 DESC`,
)
const byProvider = await query<{
provider: string
total: number
pending: number
with_carrier: number
}>(
`
SELECT o.provider,
count(*)::int AS total,
count(*) FILTER (WHERE oi.fulfillment_status = 'pending')::int AS pending,
count(*) FILTER (
WHERE EXISTS (SELECT 1 FROM work_orders wo WHERE wo.order_item_id = oi.id)
OR EXISTS (SELECT 1 FROM fulfillment_tasks ft WHERE ft.order_item_id = oi.id)
)::int AS with_carrier
FROM order_items oi
JOIN orders o ON o.id = oi.order_id
WHERE COALESCE(o.paid_at, o.created_at) >= '2026-10-31 00:00:00+08'
GROUP BY 1
ORDER BY 2 DESC
`,
)
console.log(
JSON.stringify(
{
pending: pending.rows[0],
channelCarrier: channelCarrier.rows,
internalSku: internalSku.rows[0],
rules: rules.rows,
duplicateProducts: duplicateProducts.rows[0],
capabilities: capabilities.rows,
byProvider: byProvider.rows,
},
null,
2,
),
)
}
main()
.catch((error) => {
console.error(error)
process.exitCode = 1
})
.finally(() => closeDb())