import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, of, BehaviorSubject } from 'rxjs'; import { delay, map, catchError } from 'rxjs/operators'; // 通知类型枚举 export enum NotificationType { PAYMENT_COMPLETED = 'payment_completed', IMAGE_UNLOCKED = 'image_unlocked', SETTLEMENT_CONFIRMED = 'settlement_confirmed', REMINDER = 'reminder', SYSTEM = 'system' } // 通知渠道枚举 export enum NotificationChannel { SMS = 'sms', EMAIL = 'email', WECHAT = 'wechat', PUSH = 'push', IN_APP = 'in_app' } // 通知接口 export interface Notification { id: string; type: NotificationType; title: string; content: string; recipient: string; channels: NotificationChannel[]; templateData?: any; scheduledAt?: Date; sentAt?: Date; status: 'pending' | 'sent' | 'failed' | 'cancelled'; retryCount?: number; metadata?: any; } // 通知模板接口 export interface NotificationTemplate { id: string; type: NotificationType; name: string; title: string; content: string; channels: NotificationChannel[]; variables: string[]; } // 通知发送结果接口 export interface NotificationResult { success: boolean; notificationId: string; sentChannels: NotificationChannel[]; failedChannels: NotificationChannel[]; error?: string; } @Injectable({ providedIn: 'root' }) export class NotificationService { private http = inject(HttpClient); private notifications$ = new BehaviorSubject([]); // 预定义通知模板 private templates: NotificationTemplate[] = [ { id: 'payment_completed', type: NotificationType.PAYMENT_COMPLETED, name: '支付完成通知', title: '🎉 尾款已到账,大图已解锁!', content: ` 亲爱的客户,您好! 您的尾款支付已确认到账: • 支付方式:{{paymentMethod}} • 支付金额:¥{{amount}} • 处理时间:{{processedAt}} 🎨 高清渲染图已为您解锁,您现在可以: • 下载所有高清渲染图 • 查看全景漫游链接 • 获取设计方案文件 感谢您选择银三色摄影工作室! 如有任何问题,请随时联系我们。 `, channels: [NotificationChannel.SMS, NotificationChannel.WECHAT, NotificationChannel.IN_APP], variables: ['paymentMethod', 'amount', 'processedAt', 'customerName', 'projectName'] }, { id: 'image_unlocked', type: NotificationType.IMAGE_UNLOCKED, name: '大图解锁通知', title: '📸 您的高清渲染图已解锁', content: ` {{customerName}},您好! 项目"{{projectName}}"的高清渲染图已为您解锁: • 解锁图片数量:{{imageCount}}张 • 图片分辨率:{{resolution}} • 下载有效期:{{validUntil}} 立即下载:{{downloadLink}} 银三色摄影工作室 `, channels: [NotificationChannel.SMS, NotificationChannel.EMAIL, NotificationChannel.IN_APP], variables: ['customerName', 'projectName', 'imageCount', 'resolution', 'validUntil', 'downloadLink'] }, { id: 'settlement_reminder', type: NotificationType.REMINDER, name: '结算提醒', title: '💰 尾款结算提醒', content: ` {{customerName}},您好! 您的项目"{{projectName}}"已完成,请及时结算尾款: • 应付金额:¥{{amount}} • 截止日期:{{dueDate}} 支付完成后,我们将立即为您解锁高清渲染图。 银三色摄影工作室 `, channels: [NotificationChannel.SMS, NotificationChannel.WECHAT], variables: ['customerName', 'projectName', 'amount', 'dueDate'] } ]; /** * 发送支付完成通知 */ sendPaymentCompletedNotification(data: { recipient: string; paymentMethod: string; amount: number; customerName: string; projectName: string; channels?: NotificationChannel[]; }): Observable { console.log('发送支付完成通知:', data); const template = this.getTemplate(NotificationType.PAYMENT_COMPLETED); if (!template) { return of({ success: false, notificationId: '', sentChannels: [], failedChannels: [], error: '未找到通知模板' }); } const notification: Notification = { id: `notification_${Date.now()}`, type: NotificationType.PAYMENT_COMPLETED, title: template.title, content: this.renderTemplate(template.content, { paymentMethod: data.paymentMethod, amount: data.amount.toString(), processedAt: new Date().toLocaleString(), customerName: data.customerName, projectName: data.projectName }), recipient: data.recipient, channels: data.channels || template.channels, templateData: data, status: 'pending' }; return this.sendNotification(notification); } /** * 发送大图解锁通知 */ sendImageUnlockedNotification(data: { recipient: string; customerName: string; projectName: string; imageCount: number; resolution: string; validUntil: string; downloadLink: string; channels?: NotificationChannel[]; }): Observable { console.log('发送大图解锁通知:', data); const template = this.getTemplate(NotificationType.IMAGE_UNLOCKED); if (!template) { return of({ success: false, notificationId: '', sentChannels: [], failedChannels: [], error: '未找到通知模板' }); } const notification: Notification = { id: `notification_${Date.now()}`, type: NotificationType.IMAGE_UNLOCKED, title: template.title, content: this.renderTemplate(template.content, data), recipient: data.recipient, channels: data.channels || template.channels, templateData: data, status: 'pending' }; return this.sendNotification(notification); } /** * 发送结算提醒通知 */ sendSettlementReminderNotification(data: { recipient: string; customerName: string; projectName: string; amount: number; dueDate: string; channels?: NotificationChannel[]; }): Observable { console.log('发送结算提醒通知:', data); const template = this.getTemplate(NotificationType.REMINDER); if (!template) { return of({ success: false, notificationId: '', sentChannels: [], failedChannels: [], error: '未找到通知模板' }); } const notification: Notification = { id: `notification_${Date.now()}`, type: NotificationType.REMINDER, title: template.title, content: this.renderTemplate(template.content, { customerName: data.customerName, projectName: data.projectName, amount: data.amount.toString(), dueDate: data.dueDate }), recipient: data.recipient, channels: data.channels || template.channels, templateData: data, status: 'pending' }; return this.sendNotification(notification); } /** * 发送通知 */ private sendNotification(notification: Notification): Observable { console.log('发送通知:', notification); // 模拟发送过程 return of(null).pipe( delay(1500), // 模拟网络延迟 map(() => { // 模拟发送结果 const successRate = 0.95; // 95%成功率 const success = Math.random() < successRate; if (success) { notification.status = 'sent'; notification.sentAt = new Date(); // 添加到通知历史 this.addToNotificationHistory(notification); return { success: true, notificationId: notification.id, sentChannels: notification.channels, failedChannels: [], }; } else { notification.status = 'failed'; notification.retryCount = (notification.retryCount || 0) + 1; return { success: false, notificationId: notification.id, sentChannels: [], failedChannels: notification.channels, error: '网络错误或服务暂时不可用' }; } }), catchError(error => { console.error('通知发送失败:', error); notification.status = 'failed'; return of({ success: false, notificationId: notification.id, sentChannels: [], failedChannels: notification.channels, error: error.message || '发送失败' }); }) ); } /** * 获取通知模板 */ private getTemplate(type: NotificationType): NotificationTemplate | undefined { return this.templates.find(t => t.type === type); } /** * 渲染模板内容 */ private renderTemplate(template: string, data: any): string { let rendered = template; Object.keys(data).forEach(key => { const placeholder = `{{${key}}}`; rendered = rendered.replace(new RegExp(placeholder, 'g'), data[key]); }); return rendered; } /** * 添加到通知历史 */ private addToNotificationHistory(notification: Notification): void { const current = this.notifications$.value; this.notifications$.next([notification, ...current]); } /** * 获取通知历史 */ getNotificationHistory(): Observable { return this.notifications$.asObservable(); } /** * 获取通知统计 */ getNotificationStatistics(): Observable<{ total: number; sent: number; failed: number; pending: number; byType: { [key: string]: number }; byChannel: { [key: string]: number }; }> { return this.notifications$.pipe( map(notifications => { const total = notifications.length; const sent = notifications.filter(n => n.status === 'sent').length; const failed = notifications.filter(n => n.status === 'failed').length; const pending = notifications.filter(n => n.status === 'pending').length; const byType: { [key: string]: number } = {}; const byChannel: { [key: string]: number } = {}; notifications.forEach(n => { byType[n.type] = (byType[n.type] || 0) + 1; n.channels.forEach(channel => { byChannel[channel] = (byChannel[channel] || 0) + 1; }); }); return { total, sent, failed, pending, byType, byChannel }; }) ); } /** * 重试失败的通知 */ retryFailedNotification(notificationId: string): Observable { const notifications = this.notifications$.value; const notification = notifications.find(n => n.id === notificationId); if (!notification || notification.status !== 'failed') { return of({ success: false, notificationId, sentChannels: [], failedChannels: [], error: '通知不存在或状态不正确' }); } notification.status = 'pending'; return this.sendNotification(notification); } /** * 获取支持的通知渠道 */ getSupportedChannels(): NotificationChannel[] { return Object.values(NotificationChannel); } /** * 获取通知模板列表 */ getTemplates(): NotificationTemplate[] { return [...this.templates]; } }