import db from '../config/database'; import { logger } from '../utils/logger'; export interface SupplierPerformance { id: string; tenantId: string; name: string; ratingScore: number; avgDeliveryDays: number; defectRate: number; performanceHistory: any; } /** * [BIZ_TRADE_04] 供应商风险评级与自动优选服务 * @description 建立供应商画像,基于交期、质量、价格波动自动优选最优货源 */ export class SupplierService { private static readonly TABLE_NAME = 'cf_suppliers'; /** * 初始化数据库表 */ static async initTable() { const hasTable = await db.schema.hasTable(this.TABLE_NAME); if (!hasTable) { 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).notNullable().index(); // [CORE_SEC_45] 租户隔离 table.string('name', 128).notNullable(); table.decimal('rating_score', 10, 2).defaultTo(80.00); table.decimal('avg_delivery_days', 10, 2).defaultTo(5.00); table.decimal('defect_rate', 10, 4).defaultTo(0.0000); table.decimal('price_stability', 10, 2).defaultTo(0.95); // [BIZ_SUP_20] 价格稳定性 (0-1) table.decimal('avg_response_hours', 10, 2).defaultTo(2.00); // [BIZ_SUP_20] 响应速度 (小时) table.boolean('isSourceFactory').defaultTo(false); table.string('risk_level', 20).defaultTo('LOW'); // [BIZ_OPS_157] 经营风险级别 table.text('legal_notices').nullable(); // [BIZ_OPS_157] 法务/工商风险详情 table.json('performance_history').nullable(); table.timestamps(true, true); // 增加复合索引:租户 + 工厂属性 table.index(['tenant_id', 'isSourceFactory'], 'idx_supplier_type'); }); console.log(`✅ Table ${this.TABLE_NAME} created`); } else { // [BIZ_OPS_157] 确保风险分析所需的列存在 const hasRisk = await db.schema.hasColumn(this.TABLE_NAME, 'risk_level'); if (!hasRisk) { await db.schema.table(this.TABLE_NAME, (table) => { table.string('risk_level', 20).defaultTo('LOW'); table.text('legal_notices').nullable(); }); logger.info(`✅ Table ${this.TABLE_NAME} updated with risk columns`); } } } /** * 记录供应商 */ static async registerSupplier(supplier: SupplierPerformance): Promise { logger.info(`[Supplier] Registering supplier: ${supplier.name} for Tenant: ${supplier.tenantId}`); await db(this.TABLE_NAME).insert({ id: supplier.id, tenant_id: supplier.tenantId, name: supplier.name, rating_score: supplier.ratingScore, avg_delivery_days: supplier.avgDeliveryDays, defect_rate: supplier.defectRate, performance_history: JSON.stringify(supplier.performanceHistory || {}), created_at: new Date(), updated_at: new Date() }); } /** * 更新供应商绩效数据 (由采购单 PO 完成后调用) */ static async updatePerformance(supplierId: string, stats: { deliveryDays: number; qualityResult: 'PASSED' | 'FAILED' }): Promise { const supplier = await db(this.TABLE_NAME).where({ id: supplierId }).first(); if (!supplier) return; const history = typeof supplier.performance_history === 'string' ? JSON.parse(supplier.performance_history) : supplier.performance_history; history.deliveries = (history.deliveries || 0) + 1; history.total_days = (history.total_days || 0) + stats.deliveryDays; if (stats.qualityResult === 'FAILED') { history.defects = (history.defects || 0) + 1; } const newAvgDelivery = history.total_days / history.deliveries; const newDefectRate = (history.defects || 0) / history.deliveries; // 综合评分公式: (1 - 破损率) * 70% + (1 / 交期) * 30% (简化版) const newScore = (1 - newDefectRate) * 70 + (1 / Math.max(newAvgDelivery, 1)) * 30; await db(this.TABLE_NAME).where({ id: supplierId }).update({ avg_delivery_days: newAvgDelivery, defect_rate: newDefectRate, rating_score: newScore, performance_history: JSON.stringify(history), updated_at: new Date() }); } /** * [BIZ_SUP_20] 实时性能指标采集 (Performance Telemetry) * @description 模拟从订单履约、客服沟通中自动提取供应商表现数据 */ static async collectRealTimeMetrics(supplierId: string, tenantId: string) { logger.info(`[TrustScore] Collecting real-time metrics for Supplier: ${supplierId}`); try { // 1. 获取该供应商近期的履约数据 const stats = await db('cf_orders') .where({ supplier_id: supplierId, tenant_id: tenantId }) .orderBy('created_at', 'desc') .limit(20) .select('logistics_cost', 'status', 'created_at', 'updated_at'); if (stats.length === 0) return; // 2. 计算平均履约时效 (模拟逻辑) const avgDelivery = stats.reduce((acc, curr) => { const days = (curr.updated_at.getTime() - curr.created_at.getTime()) / (1000 * 3600 * 24); return acc + days; }, 0) / stats.length; // 3. 计算价格稳定性 (价格标准差,模拟) const priceStability = 0.98 - (Math.random() * 0.1); // 4. 更新供应商主表 await db(this.TABLE_NAME).where({ id: supplierId }).update({ avg_delivery_days: Number(avgDelivery.toFixed(2)), price_stability: priceStability, updated_at: new Date() }); logger.info(`[TrustScore] Metrics updated for ${supplierId}: Delivery=${avgDelivery.toFixed(1)}d, Stability=${priceStability.toFixed(2)}`); } catch (err: any) { // [CORE_DIAG_01] Agent 异常自省 logger.error(`[TrustScore][WARN] Metrics collection failed: ${err.message}`); throw { category: 'Context Missing', rootCause: 'Insufficient order data for statistical analysis', mitigation: 'Wait for more orders or use industry benchmark defaults' }; } } /** * [BIZ_SUP_20] 供应商全链路信用与质量评分模型 * @description 基于多维指标 (交期、质量、响应速度、价格稳定性) 自动计算信用分 */ static async calculateSupplierScore(supplierId: string): Promise { const supplier = await db(this.TABLE_NAME).where({ id: supplierId }).first(); if (!supplier) return 0; // 1. 交期维度 (权重 30%) const deliveryScore = Math.max(0, 100 - (supplier.avg_delivery_days * 5)); // 2. 质量维度 (权重 30%) const qualityScore = (1 - supplier.defect_rate) * 100; // 3. 响应速度 (权重 20%) const responseScore = Math.max(0, 100 - (supplier.avg_response_hours * 5)); // 1小时 95, 2小时 90... // 4. 价格稳定性 (权重 20%) const stabilityScore = supplier.price_stability * 100; // 综合加权总分 const finalScore = (deliveryScore * 0.3) + (qualityScore * 0.3) + (responseScore * 0.2) + (stabilityScore * 0.2); await db(this.TABLE_NAME).where({ id: supplierId }).update({ rating_score: finalScore, updated_at: new Date() }); return finalScore; } /** * [BIZ_SUP_20] AGI 驱动的供应商信用报告 (TrustReport) * @description 生成供应商信用深度分析,用于采购路由决策支持 */ static async getSupplierTrustReport(supplierId: string): Promise { const supplier = await db(this.TABLE_NAME).where({ id: supplierId }).first(); if (!supplier) throw new Error('Supplier not found'); const score = await this.calculateSupplierScore(supplierId); // 模拟 AGI 叙事生成 (Narrative Engine) const riskLevel = score > 90 ? 'LOW' : score > 70 ? 'MEDIUM' : 'HIGH'; const narrative = `Supplier ${supplier.name} has a TrustScore of ${score.toFixed(2)}. ` + `Delivery performance is ${supplier.avg_delivery_days <= 3 ? 'EXCELLENT' : 'STABLE'}. ` + `Quality defect rate is ${(supplier.defect_rate * 100).toFixed(2)}%. ` + `Response time averages ${supplier.avg_response_hours} hours. ` + `Recommended Action: ${riskLevel === 'LOW' ? 'Whitelisted for Auto-PO' : 'Requires Human Review'}.`; return { supplierId: supplier.id, name: supplier.name, trustScore: score, riskLevel, narrative, metrics: { deliveryDays: supplier.avg_delivery_days, defectRate: supplier.defect_rate, responseHours: supplier.avg_response_hours, priceStability: supplier.price_stability }, isFactory: supplier.isSourceFactory }; } /** * [BIZ_SUP_15] 推荐最优供应商 */ static async recommendBestSupplier(productId: string, tenantId: string): Promise { logger.info(`[Supplier] Recommending best supplier for Product: ${productId}, Tenant: ${tenantId}`); const suppliers = await db(this.TABLE_NAME) .where({ tenant_id: tenantId }) .orderBy('rating_score', 'desc') .limit(1); return suppliers.length > 0 ? suppliers[0].id : null; } /** * [BIZ_AI_16-EXT] 更新供应商状态或执行切换建议 */ static async updateSupplierStatus(supplierId: string, status: string, notes?: string): Promise { logger.info(`[Supplier] Updating status for ${supplierId} to ${status}. Notes: ${notes}`); await db(this.TABLE_NAME).where({ id: supplierId }).update({ risk_level: status === 'BLOCK' ? 'HIGH' : 'LOW', legal_notices: notes, updated_at: new Date() }); } }