// SLA 服务可用性档位定义 // 参考:mail_monitor_wechat/README.md 服务可用性档位说明 export interface SlaTier { name: string availability: number penalty: number ratio: number | null // 当月总秒数占比,第五档 ratio 为 null(无上限) } export const SLA_TIERS: SlaTier[] = [ { name: '第一档', availability: 0.99, penalty: 0, ratio: 0.01 }, { name: '第二档', availability: 0.97, penalty: 0.10, ratio: 0.03 }, { name: '第三档', availability: 0.95, penalty: 0.25, ratio: 0.05 }, { name: '第四档', availability: 0.90, penalty: 0.50, ratio: 0.10 }, { name: '第五档', availability: 0, penalty: 1.00, ratio: null }, ] export interface SlaStatus { level: 'safe' | 'warning' | 'over-tier1' | 'critical' | 'excluded' color: string text: string } /** * 返回指定年月的总秒数 */ export function getMonthSeconds(year: number, month: number): number { const daysInMonth = new Date(year, month, 0).getDate() return daysInMonth * 24 * 3600 } /** * 返回第一档 SLA 截止时间 * 截止 = assign_time + 当月总秒数 × 1% */ export function getTier1Deadline(assignTime: string): Date { const at = new Date(assignTime) const monthSec = getMonthSeconds(at.getFullYear(), at.getMonth() + 1) const tier1Window = monthSec * 0.01 // 1% return new Date(at.getTime() + tier1Window * 1000) } /** * 返回第二档 SLA 截止时间(用于"严重"判定) */ export function getTier2Deadline(assignTime: string): Date { const at = new Date(assignTime) const monthSec = getMonthSeconds(at.getFullYear(), at.getMonth() + 1) const tier2Window = monthSec * 0.03 // 3% return new Date(at.getTime() + tier2Window * 1000) } /** * 根据 assign_time 和 counted_in_sla 返回 SLA 状态 */ export function getSlaStatus(assignTime: string, countedInSla: number): SlaStatus { if (countedInSla === 0) { return { level: 'excluded', color: '#94a3b8', text: '不计入SLA' } } const now = new Date() const tier1Deadline = getTier1Deadline(assignTime) const tier1Window = tier1Deadline.getTime() - new Date(assignTime).getTime() // 剩余时间(毫秒),正数表示还没超,负数表示已超 const remaining = tier1Deadline.getTime() - now.getTime() if (remaining > 0) { const remainingRatio = remaining / tier1Window if (remainingRatio > 0.5) { const h = Math.floor(remaining / 3600000) const m = Math.floor((remaining % 3600000) / 60000) return { level: 'safe', color: '#22c55e', text: `还剩 ${h}h ${m}m` } } else { const h = Math.floor(remaining / 3600000) const m = Math.floor((remaining % 3600000) / 60000) return { level: 'warning', color: '#eab308', text: `还剩 ${h}h ${m}m` } } } // 已超第一档 const exceeded = Math.abs(remaining) const tier2Deadline = getTier2Deadline(assignTime) if (now < tier2Deadline) { const h = Math.floor(exceeded / 3600000) const m = Math.floor((exceeded % 3600000) / 60000) return { level: 'over-tier1', color: '#f97316', text: `超时 ${h}h ${m}m` } } // 已超第二档(严重) const h = Math.floor(exceeded / 3600000) const m = Math.floor((exceeded % 3600000) / 60000) return { level: 'critical', color: '#ef4444', text: `超时 ${h}h ${m}m` } }