Files
makemd/server/src/services/SLAScoringService.ts

112 lines
4.1 KiB
TypeScript
Raw Normal View History

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<any> {
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 };
}
}
}