import db from '../config/database'; import { logger } from '../utils/logger'; import { DecisionExplainabilityEngine } from '../core/ai/DecisionExplainabilityEngine'; export interface TicketSentiment { ticketId: string; sentimentScore: number; // 0 (negative) to 1 (positive) urgency: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'; keywords: string[]; } /** * [BIZ_OPS_135] 智能工单优先级调度建议 (Support Queue) * @description 核心逻辑:分析工单内容的情感极性与关键词,自动识别“紧急差评”或“退款威胁”,并建议优先处理。 */ export class PriorityTicketService { private static readonly PRIORITY_AUDIT_TABLE = 'cf_ticket_priority_audit'; /** * 初始化数据库表 */ static async initTable() { const hasTable = await db.schema.hasTable(this.PRIORITY_AUDIT_TABLE); if (!hasTable) { await db.schema.createTable(this.PRIORITY_AUDIT_TABLE, (table) => { table.increments('id').primary(); table.string('tenant_id', 64).index(); table.string('ticket_id', 64).index(); table.string('priority_suggested', 16); table.text('reason'); table.string('status', 32).defaultTo('PENDING_REVIEW'); table.timestamp('created_at').defaultTo(db.fn.now()); }); } } /** * 优先调度工单 (BIZ_OPS_135) */ static async prioritizeTickets(tenantId: string): Promise { logger.info(`[TicketPriority] Analyzing tickets for Tenant: ${tenantId}`); try { // 1. 获取该租户下所有未解决的工单 (模拟查询) const tickets = await db('cf_support_tickets') .where({ tenant_id: tenantId, status: 'OPEN' }) .limit(50); const prioritized = []; for (const ticket of tickets) { // 2. 情感与关键词分析 (模拟) const sentiment = await this.analyzeTicketSentiment(ticket.content); // 3. 优先级策略:情感极低 (< 0.2) 或 包含 "refund", "scam" let priority: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL' = 'LOW'; if (sentiment.sentimentScore < 0.2 || sentiment.keywords.some(k => ['refund', 'scam', 'bad', 'fraud'].includes(k.toLowerCase()))) { priority = 'CRITICAL'; } else if (sentiment.sentimentScore < 0.5) { priority = 'HIGH'; } if (priority === 'CRITICAL' || priority === 'HIGH') { const reason = `Ticket identified as ${priority} based on negative sentiment (${sentiment.sentimentScore.toFixed(2)}) ` + `and keywords: [${sentiment.keywords.join(', ')}].`; // 4. 生成优先处理建议 (Suggestion-First) const [suggestionId] = await db(this.PRIORITY_AUDIT_TABLE).insert({ tenant_id: tenantId, ticket_id: ticket.id, priority_suggested: priority, reason: reason, status: 'PENDING_REVIEW', created_at: new Date() }); // 5. [UX_XAI_01] 记录决策证据链 await DecisionExplainabilityEngine.logDecision({ tenantId, module: 'SUPPORT_TICKET', resourceId: String(suggestionId), decisionType: 'PRIORITY_SCHEDULING_SUGGESTION', causalChain: reason, factors: [ { name: 'SentimentScore', value: sentiment.sentimentScore, weight: 0.6, impact: 'NEGATIVE' }, { name: 'KeywordMatch', value: sentiment.keywords.length, weight: 0.4, impact: 'NEGATIVE' } ], traceId: 'ticket-priority-' + Date.now() }); prioritized.push({ ticketId: ticket.id, priority, suggestionId }); } } return { success: true, count: prioritized.length, prioritized, status: 'PENDING_REVIEW' }; } catch (err: any) { logger.error(`[TicketPriority][WARN] Prioritization failed: ${err.message}`); return { success: false, error: err.message }; } } private static async analyzeTicketSentiment(content: string): Promise { // 实际业务中应调用 AIService 或专门的情感分析接口 const score = Math.random(); // 模拟得分 const keywords = ['refund', 'wait', 'shipping'].filter(() => Math.random() > 0.5); return { ticketId: 'mock', sentimentScore: score, urgency: 'MEDIUM', keywords }; } }