123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- 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<Notification[]>([]);
-
- // 预定义通知模板
- 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<NotificationResult> {
- 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<NotificationResult> {
- 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<NotificationResult> {
- 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<NotificationResult> {
- 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<Notification[]> {
- 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<NotificationResult> {
- 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];
- }
- }
|