import db from '../config/database'; import { logger } from '../utils/logger'; import { FXHedgingService } from './FXHedgingService'; import { TaxService } from './TaxService'; export interface OrderRecord { id: string; tenantId: string; shopId?: string; taskId?: string; traceId: string; platform: string; productId: string; sellingPrice: number; purchasePrice: number; countryCode: string; logisticsCost: number; adBudget: number; status: string; } /** * [BIZ_FIN_01] 财务结算与对账服务 * @description 处理订单回流、利差核算与账单生成 */ export class FinanceService { private static readonly TABLE_NAME = 'cf_orders'; private static readonly TRANSACTION_TABLE = 'cf_transactions'; /** * 初始化数据库表 */ static async initTable() { const hasOrderTable = await db.schema.hasTable(this.TABLE_NAME); if (!hasOrderTable) { console.log(`📦 Creating ${this.TABLE_NAME} table...`); await db.schema.createTable(this.TABLE_NAME, (table) => { table.string('id', 64).primary(); table.string('tenant_id', 64).index(); table.string('platform', 32); table.string('product_id', 64); table.decimal('selling_price', 10, 2); table.decimal('purchase_price', 10, 2); table.string('country_code', 8); table.decimal('logistics_cost', 10, 2); table.decimal('ad_budget', 10, 2); table.string('status', 32); table.timestamps(true, true); }); } const hasTransactionTable = await db.schema.hasTable(this.TRANSACTION_TABLE); if (!hasTransactionTable) { console.log(`📦 Creating ${this.TRANSACTION_TABLE} table...`); await db.schema.createTable(this.TRANSACTION_TABLE, (table) => { table.increments('id').primary(); table.string('tenant_id', 64).index(); table.string('order_id', 64).nullable(); table.decimal('amount', 10, 2); table.string('currency', 8).defaultTo('USD'); table.string('transaction_type', 32); table.string('trace_id', 64); table.json('metadata').nullable(); table.timestamp('created_at').defaultTo(db.fn.now()); }); } } /** * 记录订单 */ static async recordOrder(order: OrderRecord): Promise { const { tenantId, shopId, taskId, traceId } = order; // 1. 插入订单主表 await db(this.TABLE_NAME).insert({ id: order.id, tenant_id: order.tenantId, platform: order.platform, product_id: order.productId, selling_price: order.sellingPrice, purchase_price: order.purchasePrice, country_code: order.countryCode, logistics_cost: order.logisticsCost, ad_budget: order.adBudget, status: order.status, created_at: new Date(), updated_at: new Date() }); // 2. 实时税务计提 (BIZ_FIN_02) const taxInfo = TaxService.calculateTax({ countryCode: order.countryCode, baseAmount: order.sellingPrice, currency: 'USD' }); await this.accrueTax(order.id, { tenantId, amount: taxInfo.totalTax, currency: 'USD', type: taxInfo.isIOSS ? 'IOSS_VAT' : 'STANDARD_VAT', traceId }); // 3. 汇率自动对冲 (BIZ_FIN_03) await FXHedgingService.autoHedge({ tenantId, pair: 'USD/CNY', amount: order.sellingPrice, traceId }); } /** * [BIZ_FIN_04] 记录财务交易流水 */ static async recordTransaction(params: { tenantId: string; amount: number; currency?: string; type: 'ORDER_REVENUE' | 'LOGISTICS_COST' | 'PLATFORM_FEE' | 'REFUND' | 'COMMISSION' | 'INCOME'; category?: string; orderId?: string; traceId?: string; metadata?: any; }) { logger.info(`[Finance] Recording transaction: ${params.type} for Order ${params.orderId || 'N/A'} (${params.amount} ${params.currency || 'USD'})`); // 实际业务中需插入 cf_transactions 表 await db('cf_transactions').insert({ tenant_id: params.tenantId, order_id: params.orderId || null, amount: params.amount, currency: params.currency || 'USD', transaction_type: params.type, trace_id: params.traceId || 'system', metadata: params.metadata ? JSON.stringify(params.metadata) : null, created_at: new Date() }); } /** * [BIZ_FIN_02] 实时税务计提记录 * @description 将税务计提记录到 cf_tax_accruals 表,用于后期对账与报关 */ private static async accrueTax(orderId: string, params: { tenantId: string; amount: number; currency: string; type: string; traceId: string; }): Promise { logger.info(`[Finance] Accruing tax for order ${orderId}: ${params.amount} ${params.currency} (${params.type})`); // 实际业务中需插入 cf_tax_accruals await db('cf_tax_accruals').insert({ order_id: orderId, tenant_id: params.tenantId, amount: params.amount, currency: params.currency, tax_type: params.type, status: 'ACCRUED', trace_id: params.traceId, created_at: new Date(), updated_at: new Date() }); } /** * [BIZ_FIN_06] 全自动化财务利润对账 * @description 自动勾稽订单、采购成本、物流费与税金,生成最终利润报表 */ static async performAutoProfitRecon(tenantId: string, shopId: string): Promise { logger.info(`[Finance] Performing auto profit reconciliation for Tenant: ${tenantId}, Shop: ${shopId}`); // 1. 获取所有已完成且未对账的订单 const orders = await db('cf_orders') .where({ tenant_id: tenantId, shop_id: shopId, status: 'PUBLISHED' }) // 假设 PUBLISHED 状态代表已成单 .whereNull('reconciled_at'); const results = []; for (const order of orders) { try { // 2. 聚合各项成本 const taxAccrual = await db('cf_tax_accruals').where({ order_id: order.id }).first(); const purchaseOrder = await db('cf_purchase_orders').where({ product_id: order.product_id, tenant_id: tenantId }).first(); const logisticsRoute = await db('cf_logistics_routes').where({ tenant_id: tenantId }).first(); // 简化取第一个 const purchaseCost = purchaseOrder ? purchaseOrder.total_amount / purchaseOrder.quantity : 0; const taxAmount = taxAccrual ? taxAccrual.amount : 0; const logisticsCost = logisticsRoute ? logisticsRoute.cost_per_kg * 0.5 : 5; // 假设 0.5kg const netProfit = order.selling_price - purchaseCost - logisticsCost - taxAmount; // 3. 记录对账结果 await db('cf_orders').where({ id: order.id }).update({ net_profit: netProfit, reconciled_at: new Date(), updated_at: new Date() }); results.push({ orderId: order.id, netProfit }); } catch (err: any) { logger.error(`[Finance] Recon failed for Order ${order.id}: ${err.message}`); } } return { totalReconciled: results.length, details: results }; } /** * 获取账单回放 */ static async getBillPlayback(tenantId: string, shopId: string, orderId: string): Promise { const order = await db(this.TABLE_NAME) .where({ id: orderId, tenant_id: tenantId }) .first(); if (!order) return null; return { orderId: order.id, timeline: [ { event: 'ORDER_CREATED', time: order.created_at, amount: order.selling_price }, { event: 'PURCHASE_COMPLETED', time: order.updated_at, amount: -order.purchase_price }, { event: 'LOGISTICS_DEDUCTED', time: order.updated_at, amount: -order.logistics_cost } ], finalProfit: order.selling_price - order.purchase_price - order.logistics_cost }; } }