- 新增文档模板和导航结构 - 实现服务器基础API路由和控制器 - 添加扩展插件配置和前端框架 - 引入多租户和权限管理模块 - 集成日志和数据库配置 - 添加核心业务模型和类型定义
119 lines
4.3 KiB
TypeScript
119 lines
4.3 KiB
TypeScript
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<any> {
|
|
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<TicketSentiment> {
|
|
// 实际业务中应调用 AIService 或专门的情感分析接口
|
|
const score = Math.random(); // 模拟得分
|
|
const keywords = ['refund', 'wait', 'shipping'].filter(() => Math.random() > 0.5);
|
|
return {
|
|
ticketId: 'mock',
|
|
sentimentScore: score,
|
|
urgency: 'MEDIUM',
|
|
keywords
|
|
};
|
|
}
|
|
}
|