import db from '../config/database'; import { logger } from '../utils/logger'; import { SupplierService } from './SupplierService'; import { DecisionExplainabilityEngine } from '../core/ai/DecisionExplainabilityEngine'; import { AdviceService } from '../domains/Strategy/AdviceService'; /** * [BIZ_OPS_132] 供应商响应时效动态分级 (Supplier SLA) * @description 核心逻辑:基于响应速度 (avg_response_hours) 与交期 (avg_delivery_days) 自动标记低效方,并提供切换建议。 */ export class SLAScoringService { private static readonly AUDIT_TABLE = 'cf_sourcing_audit'; /** * 审计供应商 SLA 并生成警告/建议 (BIZ_OPS_132) */ static async auditSupplierSLA(tenantId: string, supplierId: string): Promise { logger.info(`[SLAScoring] Auditing SLA for Supplier: ${supplierId}, Tenant: ${tenantId}`); try { // 1. 获取供应商最新绩效报告 const supplierReport = await SupplierService.getSupplierTrustReport(supplierId); const metrics = supplierReport.metrics; const score = supplierReport.trustScore; // 2. SLA 阈值校验 (务实点) // 响应速度 > 24h 或 交期 > 10d 触发预警 let slaBreached = false; let breachReason = ''; if (metrics.responseHours > 24) { slaBreached = true; breachReason += `Response time (${metrics.responseHours}h) exceeds 24h limit. `; } if (metrics.deliveryDays > 10) { slaBreached = true; breachReason += `Delivery time (${metrics.deliveryDays}d) exceeds 10d limit. `; } if (score < 50) { slaBreached = true; breachReason += `Overall TrustScore (${score.toFixed(1)}) is dangerously low. `; } if (slaBreached) { logger.warn(`[SLAScoring] SLA Breach detected for ${supplierId}: ${breachReason}`); // 3. 寻找同租户下同类目更好的备选供应商 (模拟逻辑) const alternatives = await db('cf_suppliers') .where({ tenant_id: tenantId }) .where('rating_score', '>', score + 10) .orderBy('rating_score', 'desc') .limit(1); const bestAlt = alternatives[0]; const reason = `SLA BREACH: ${breachReason} ` + (bestAlt ? `Recommended to switch to ${bestAlt.name} (Score: ${bestAlt.rating_score.toFixed(1)}).` : 'Immediate review required.'); // 4. 生成建议 (Suggestion-First) -> 统一至 AdviceService const suggestionId = await AdviceService.recordAdvice({ tenantId, shopId: 'GLOBAL', // 供应商审核通常是全局的 traceId: 'sla-audit-' + Date.now(), insight: { type: 'SUPPLIER', title: `SLA Breach: ${supplierReport.name}`, description: reason, impact: 'high', confidence: 0.95, suggestedAction: bestAlt ? `SWITCH_TO_${bestAlt.id}` : 'MANUAL_REVIEW', metadata: { supplierId, bestAlternativeId: bestAlt?.id, metrics } } }); // 5. [UX_XAI_01] 记录决策证据链 await DecisionExplainabilityEngine.logDecision({ tenantId, module: 'SUPPLIER_SLA', resourceId: String(suggestionId), decisionType: 'SLA_BREACH_WARNING', causalChain: reason, factors: [ { name: 'ResponseHours', value: metrics.responseHours, weight: 0.4, impact: 'NEGATIVE' }, { name: 'DeliveryDays', value: metrics.deliveryDays, weight: 0.4, impact: 'NEGATIVE' }, { name: 'TrustScore', value: score, weight: 0.2, impact: 'NEGATIVE' } ], traceId: 'sla-audit-' + Date.now() }); return { success: true, suggestionId, slaBreached, reason, bestAlternative: bestAlt?.name, status: 'PENDING_REVIEW' }; } return { success: true, slaBreached: false, message: 'SLA compliant' }; } catch (err: any) { logger.error(`[SLAScoring][WARN] SLA audit failed: ${err.message}`); return { success: false, error: err.message }; } } }