import * as vscode from 'vscode'; import * as path from 'path'; // 尝试加载 node-notifier,如果失败则使用 null let notifier: any = null; try { notifier = require('node-notifier'); console.log('[NotificationService] node-notifier 加载成功'); } catch (error) { console.log('[NotificationService] node-notifier 加载失败,将只使用 VS Code 内置通知'); } /** * 通知类型枚举 */ export enum NotificationType { INFO = 'info', SUCCESS = 'success', WARNING = 'warning', ERROR = 'error' } /** * 通知选项接口 */ export interface NotificationOptions { /** 通知标题 */ title: string; /** 通知消息 */ message: string; /** 通知类型 */ type?: NotificationType; /** 是否播放声音 */ sound?: boolean; /** 超时时间(秒),0 表示不自动消失 */ timeout?: number; /** 自定义图标路径 */ icon?: string; /** 点击通知时的回调 */ onClick?: () => void; } /** * 系统通知服务类 */ export class NotificationService { private static instance: NotificationService; private readonly extensionPath: string; private readonly iconPath: string; private lastNotificationTime: Map = new Map(); private readonly DEBOUNCE_INTERVAL = 3000; // 3 秒防抖 private constructor(context: vscode.ExtensionContext) { this.extensionPath = context.extensionPath; this.iconPath = path.join(this.extensionPath, 'media', 'icon.png'); console.log('[NotificationService] 初始化通知服务'); console.log('[NotificationService] 扩展路径:', this.extensionPath); console.log('[NotificationService] 图标路径:', this.iconPath); } /** * 获取单例实例 */ public static getInstance(context?: vscode.ExtensionContext): NotificationService { if (!NotificationService.instance && context) { NotificationService.instance = new NotificationService(context); } return NotificationService.instance; } /** * 检查是否启用系统通知 */ private isSystemNotificationEnabled(): boolean { const config = vscode.workspace.getConfiguration('ic-coder'); return config.get('enableSystemNotification', true); } /** * 检查是否应该发送通知(防抖) */ private shouldSendNotification(key: string): boolean { const now = Date.now(); const lastTime = this.lastNotificationTime.get(key) || 0; if (now - lastTime < this.DEBOUNCE_INTERVAL) { return false; } this.lastNotificationTime.set(key, now); return true; } /** * 发送系统通知 */ public sendNotification(options: NotificationOptions): void { console.log('[NotificationService] ========== 开始发送通知 =========='); console.log('[NotificationService] 通知选项:', options); // 检查用户配置 if (!this.isSystemNotificationEnabled()) { console.log('[NotificationService] 系统通知已禁用'); return; } console.log('[NotificationService] 系统通知已启用'); const { title, message, type = NotificationType.INFO, onClick } = options; // 防抖检查 const notificationKey = `${title}-${message}`; if (!this.shouldSendNotification(notificationKey)) { console.log('[NotificationService] 通知被防抖机制拦截'); return; } console.log('[NotificationService] 通过防抖检查'); // 如果 node-notifier 不可用,直接使用 VS Code 内置通知 if (!notifier) { console.log('[NotificationService] node-notifier 不可用,使用 VS Code 内置通知'); this.showVSCodeNotification(title, message, type, onClick); return; } // 使用 node-notifier 发送系统通知 console.log('[NotificationService] 使用 node-notifier 发送系统通知'); try { const notificationConfig: any = { title: title, message: message, sound: true, wait: false, timeout: 10, appID: 'IC Coder' }; // Windows 特定配置 if (process.platform === 'win32') { notificationConfig.icon = this.iconPath; console.log('[NotificationService] Windows 平台,图标路径:', this.iconPath); } console.log('[NotificationService] 通知配置:', notificationConfig); notifier.notify(notificationConfig, (err: any, response: any, metadata: any) => { if (err) { console.error('[NotificationService] ❌ node-notifier 失败:', err); } else { console.log('[NotificationService] ✅ node-notifier 成功'); console.log('[NotificationService] 响应:', response); console.log('[NotificationService] 元数据:', metadata); } }); if (onClick) { notifier.on('click', () => { console.log('[NotificationService] 用户点击了系统通知'); onClick(); }); } } catch (error) { console.error('[NotificationService] ❌ node-notifier 异常:', error); // 如果系统通知失败,显示 VS Code 内置通知作为备用 console.log('[NotificationService] 系统通知失败,显示 VS Code 内置通知'); this.showVSCodeNotification(title, message, type, onClick); } } /** * 显示 VS Code 内置通知 */ private showVSCodeNotification( title: string, message: string, type: NotificationType, onClick?: () => void ): void { const fullMessage = `${title}: ${message}`; console.log('[NotificationService] 显示 VS Code 通知:', fullMessage); let notificationPromise: Thenable; switch (type) { case NotificationType.ERROR: notificationPromise = vscode.window.showErrorMessage(fullMessage, '查看详情'); break; case NotificationType.WARNING: notificationPromise = vscode.window.showWarningMessage(fullMessage, '查看详情'); break; case NotificationType.SUCCESS: case NotificationType.INFO: default: notificationPromise = vscode.window.showInformationMessage(fullMessage, '查看详情'); break; } // 处理点击事件 if (onClick) { notificationPromise.then((selection) => { if (selection === '查看详情') { console.log('[NotificationService] 用户点击了通知'); onClick(); } }); } } /** * 发送成功通知 */ public success(title: string, message: string, onClick?: () => void): void { this.sendNotification({ title, message, type: NotificationType.SUCCESS, sound: true, timeout: 10, onClick }); } /** * 发送错误通知 */ public error(title: string, message: string, onClick?: () => void): void { this.sendNotification({ title, message, type: NotificationType.ERROR, sound: true, timeout: 15, onClick }); } /** * 发送警告通知 */ public warning(title: string, message: string, onClick?: () => void): void { this.sendNotification({ title, message, type: NotificationType.WARNING, sound: true, timeout: 10, onClick }); } /** * 发送信息通知 */ public info(title: string, message: string, onClick?: () => void): void { this.sendNotification({ title, message, type: NotificationType.INFO, sound: false, timeout: 8, onClick }); } }