/** * 本地财务流程演示数据:待分类、自动发货、人工、兜底、未分类。 * 用法:npx tsx scripts/seed-local-finance-demo.ts */ import { closeDb, query } from '../src/db/client.js' const SHOPS = ['sim-voucher', 'sim-aff', 'sim-manual', 'sim-fallback', 'sim-unmapped'] const TODAY = '2026-11-02 10:00:00+08' async function cleanup() { await query( `DELETE FROM fulfillment_tasks WHERE order_item_id IN (SELECT id FROM order_items WHERE order_id IN (SELECT id FROM orders WHERE shop_id = ANY($1)))`, [SHOPS], ) await query( `DELETE FROM cost_snapshots WHERE order_item_id IN (SELECT id FROM order_items WHERE order_id IN (SELECT id FROM orders WHERE shop_id = ANY($1)))`, [SHOPS], ) await query(`DELETE FROM sales_daily_facts WHERE shop_id = ANY($1)`, [SHOPS]) await query( 'ALTER TABLE fulfillment_state_events DISABLE TRIGGER trg_fulfillment_state_events_append_only', ) try { await query( `DELETE FROM order_items WHERE order_id IN (SELECT id FROM orders WHERE shop_id = ANY($1))`, [SHOPS], ) } finally { await query( 'ALTER TABLE fulfillment_state_events ENABLE TRIGGER trg_fulfillment_state_events_append_only', ) } await query(`DELETE FROM orders WHERE shop_id = ANY($1)`, [SHOPS]) await query( `DELETE FROM channel_product_mappings WHERE canonical_product_id IN ( SELECT id FROM product_catalog WHERE canonical_sku IN ( 'aff:SIM_AFF_1', 'man:模拟人工商品', 'tpv:模拟三方卡券', 'tpv:模拟三方卡券B' ) )`, ) await query( `DELETE FROM cost_versions WHERE canonical_product_id IN ( SELECT id FROM product_catalog WHERE canonical_sku IN ( 'aff:SIM_AFF_1', 'man:模拟人工商品', 'tpv:模拟三方卡券', 'tpv:模拟三方卡券B' ) )`, ) await query( `DELETE FROM manual_fulfillment_configs WHERE sku_key IN ('模拟人工商品', '模拟自动发货商品')`, ) await query( `DELETE FROM product_catalog WHERE canonical_sku IN ( 'aff:SIM_AFF_1', 'man:模拟人工商品', 'tpv:模拟三方卡券', 'tpv:模拟三方卡券B' )`, ) await query( `DELETE FROM internal_skus WHERE internal_sku_key IN ( canonicalize_internal_sku('模拟自动发货商品'), canonicalize_internal_sku('模拟三方卡券'), canonicalize_internal_sku('模拟人工商品') )`, ) } async function createOrder(shop: string, orderNo: string, amountFen = 1000) { const result = await query<{ id: number }>( `INSERT INTO orders (provider, platform, shop_id, platform_order_id, order_status, pay_status, total_amount, paid_at, created_at, updated_at) VALUES ('91kaquan','91kaquan',$1,$2,'paid','paid',$3,$4,NOW(),NOW()) RETURNING id`, [shop, orderNo, amountFen, TODAY], ) return Number(result.rows[0]!.id) } async function createItem(input: { orderId: number skuCode: string skuName: string channel?: string status?: string productId?: number | null }) { const result = await query<{ id: number }>( `INSERT INTO order_items ( order_id, sku_code, sku_name, quantity, revenue_amount_fen, revenue_basis, fulfillment_channel, fulfillment_status, fulfillment_source, canonical_product_id, created_at, updated_at ) VALUES ($1,$2,$3,1,1000,'order_total',$4,$5,$6,$7,NOW(),NOW()) RETURNING id`, [ input.orderId, input.skuCode, input.skuName, input.channel || null, input.status || 'pending', input.channel ? 'auto_rule' : '', input.productId || null, ], ) return Number(result.rows[0]!.id) } async function main() { await cleanup() // 自动发货演示商品(带采购价) const affProduct = await query<{ id: number }>( `INSERT INTO product_catalog (canonical_sku, canonical_name, category, unit_name, status, attributes_json, created_at, updated_at) VALUES ('aff:SIM_AFF_1','模拟自动发货商品','试玩','件','active','{}',NOW(),NOW()) RETURNING id`, ) const affProductId = Number(affProduct.rows[0]!.id) const affCostItem = await query<{ id: number }>( `SELECT id FROM cost_items WHERE code='sys_affiliate_purchase'`, ) await query( `INSERT INTO cost_versions (cost_item_id, scope_type, canonical_product_id, amount_fen, rate_bps, effective_from, status, change_reason, created_at, updated_at) VALUES ($1,'product',$2,500,0,'2026-10-01T00:00:00+08:00','active','本地演示采购价',NOW(),NOW())`, [affCostItem.rows[0]!.id, affProductId], ) const manualProduct = await query<{ id: number }>( `INSERT INTO product_catalog ( canonical_sku, canonical_name, category, unit_name, status, attributes_json, created_at, updated_at ) VALUES ( 'man:模拟人工商品', '模拟人工商品', '人工', '件', 'active', '{}'::jsonb, NOW(), NOW() ) RETURNING id`, ) const manualProductId = Number(manualProduct.rows[0]!.id) const manualCostItem = await query<{ id: number }>( `SELECT id FROM cost_items WHERE code = 'sys_manual_service'`, ) await query( `INSERT INTO cost_versions ( cost_item_id, scope_type, canonical_product_id, amount_fen, rate_bps, effective_from, status, change_reason, created_at, updated_at ) VALUES ($1, 'product', $2, 100, 0, '2026-10-01T00:00:00+08:00', 'active', '自动发货失败转人工成本', NOW(), NOW()), ($1, 'product', $3, 100, 0, '2026-10-01T00:00:00+08:00', 'active', '本地演示人工成本', NOW(), NOW())`, [manualCostItem.rows[0]!.id, affProductId, manualProductId], ) // 人工演示配置 await query( `INSERT INTO manual_fulfillment_configs ( sku_key, canonical_product_id, platform_key, credit_quantity, cost_mode, enabled, note, created_at, updated_at ) VALUES ('模拟自动发货商品', $1, 'nanhang', 100, 'fixed', TRUE, '自动发货兜底', NOW(), NOW()), ('模拟人工商品', $2, 'nanhang', 100, 'fixed', TRUE, '本地演示', NOW(), NOW())`, [affProductId, manualProductId], ) await query( ` INSERT INTO sku_channel_routes ( internal_sku_id, channel, route_role, priority, enabled, canonical_product_id, carrier_config_json, source, created_at, updated_at ) SELECT sku_id, channel, route_role, priority, TRUE, product_id, config_json, 'demo', NOW(), NOW() FROM ( VALUES (ensure_canonical_internal_sku('模拟自动发货商品'), 'affiliate_dash', 'primary', 10, $1::bigint, '{"affiliateSku":"SIM_AFF_1"}'::jsonb), (ensure_canonical_internal_sku('模拟自动发货商品'), 'manual', 'fallback', 90, $1::bigint, '{}'::jsonb), (ensure_canonical_internal_sku('模拟人工商品'), 'manual', 'primary', 10, $2::bigint, '{}'::jsonb) ) demo(sku_id, channel, route_role, priority, product_id, config_json) ON CONFLICT (internal_sku_id, channel) DO UPDATE SET route_role = EXCLUDED.route_role, priority = EXCLUDED.priority, enabled = TRUE, canonical_product_id = EXCLUDED.canonical_product_id, carrier_config_json = EXCLUDED.carrier_config_json, source = EXCLUDED.source, updated_at = NOW() `, [affProductId, manualProductId], ) // 1. 三方卡券:待分类,指定后自动建商品 const voucherOrder = await createOrder('sim-voucher', 'SIM-VOUCHER-1', 1950) await createItem({ orderId: voucherOrder, skuCode: 'SIMV001', skuName: '模拟三方卡券' }) // 2. 自动发货:规则命中 const affOrder = await createOrder('sim-aff', 'SIM-AFF-1', 1000) await createItem({ orderId: affOrder, skuCode: 'SIM_AFF_1', skuName: '模拟自动发货商品' }) // 3. 人工:待分类,指定人工后挂配置 const manualOrder = await createOrder('sim-manual', 'SIM-MANUAL-1', 1000) await createItem({ orderId: manualOrder, skuCode: 'SIMM001', skuName: '模拟人工商品' }) // 4. 自动发货失败:应转人工并冲销采购成本 const fallbackOrder = await createOrder('sim-fallback', 'SIM-FALLBACK-1', 1000) const fallbackProduct = await query<{ id: number }>( `SELECT id FROM product_catalog WHERE canonical_sku='aff:rock_black_tshirt' LIMIT 1`, ) const fallbackItem = await createItem({ orderId: fallbackOrder, skuCode: 'SIMFB001', skuName: '黑色ROCK长袖T恤', channel: 'affiliate_dash', status: 'assigned', productId: fallbackProduct.rows[0]?.id, }) await query( `INSERT INTO fulfillment_tasks (order_id, order_item_id, unit_index, task_no, provider, platform, shop_id, platform_order_id, profile_id, executor_key, task_status, delivery_status, created_at, updated_at) VALUES ($1,$2,1,'SIM-FB-TASK','91kaquan','kuaishou','sim-fallback','SIM-FALLBACK-1',3,'affiliate_dash','closed','failed',NOW(),NOW())`, [fallbackOrder, fallbackItem], ) // 5. 未分类:无规则、无配置 const unmappedOrder = await createOrder('sim-unmapped', 'SIM-UNMAPPED-1', 1000) await createItem({ orderId: unmappedOrder, skuCode: 'SIMUNMAPPED001', skuName: '模拟未分类商品' }) console.log( JSON.stringify( { shops: SHOPS, voucherOrder, affOrder, manualOrder, fallbackOrder, unmappedOrder, note: '等待调度器处理;可在后台履约渠道-待分类队列、商品与成本中心观察', }, null, 2, ), ) } main() .catch((error) => { console.error(error) process.exitCode = 1 }) .finally(() => closeDb())