# 通知引擎设计文档

## 📋 系统概述

### 核心目标

1. **多渠道支持**: 邮件、Teams、短信、WebSocket（可扩展）
2. **模板化**: 支持动态模板和变量替换
3. **异步处理**: 使用消息队列，不阻塞主业务
4. **可靠性**: 失败重试、状态追踪
5. **易用性**: 简单的 API，装饰器支持

---

## 🏗️ 架构设计

### 三层架构

```
┌─────────────────────────────────────────────────────────┐
│                   业务层 (Business)                      │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │  审批通知     │  │  任务提醒     │  │  系统告警     │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│                 通知引擎层 (Engine)                       │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │  通知服务     │  │  模板引擎     │  │  队列管理     │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │  重试机制     │  │  状态追踪     │  │  批量发送     │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│                 适配器层 (Adapters)                       │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │  邮件适配器   │  │  Teams适配器  │  │  短信适配器   │  │
│  │  (Nodemailer)│  │  (MS Graph)  │  │  (Aliyun)    │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  │
└─────────────────────────────────────────────────────────┘
```

---

## 📊 数据模型

### 1. NotificationTemplate (通知模板)

```prisma
model NotificationTemplate {
  id          String   @id @default(uuid())
  
  // 模板信息
  code        String   @unique           // 模板代码
  name        String                     // 模板名称
  description String?                    // 描述
  
  // 渠道类型
  channel     NotificationChannel        // EMAIL, TEAMS, SMS, WEBSOCKET
  
  // 模板内容
  subject     String?                    // 邮件主题 / Teams标题
  template    String   @db.Text          // 模板内容（支持变量）
  variables   String[]                   // 可用变量列表
  
  // 配置
  priority    NotificationPriority @default(NORMAL)
  isActive    Boolean  @default(true)
  
  // 时间戳
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  
  @@map("notification_templates")
}
```

### 2. NotificationLog (通知日志)

```prisma
model NotificationLog {
  id          String   @id @default(uuid())
  
  // 接收者
  recipientId String                     // 接收者用户ID
  recipient   User     @relation(...)
  recipientEmail String?                 // 接收者邮箱
  
  // 通知内容
  channel     NotificationChannel
  templateId  String?                    // 使用的模板ID
  subject     String?
  content     String   @db.Text
  variables   Json?                      // 实际变量值
  
  // 状态
  status      NotificationStatus @default(PENDING)
  errorMessage String?  @db.Text
  sentAt      DateTime?
  
  // 重试
  retryCount  Int      @default(0)
  maxRetries  Int      @default(3)
  nextRetryAt DateTime?
  
  // 元数据
  metadata    Json?                      // 额外信息
  
  // 时间戳
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  
  @@map("notification_logs")
  @@index([recipientId, createdAt])
  @@index([status])
  @@index([channel])
}
```

### 3. 枚举

```prisma
enum NotificationChannel {
  EMAIL
  TEAMS
  SMS
  WEBSOCKET
  IN_APP
}

enum NotificationStatus {
  PENDING     // 待发送
  SENDING     // 发送中
  SENT        // 已发送
  FAILED      // 失败
  CANCELLED   // 已取消
}

enum NotificationPriority {
  HIGH
  NORMAL
  LOW
}
```

---

## 🎯 核心功能

### 1. 发送通知

```typescript
// 简单发送
await notificationService.send({
  channel: 'EMAIL',
  to: 'user@example.com',
  subject: '审批通知',
  content: '您有一个新的审批任务',
});

// 使用模板
await notificationService.sendFromTemplate({
  templateCode: 'APPROVAL_PENDING',
  to: 'user@example.com',
  variables: {
    userName: '张三',
    taskName: '报销审批',
    amount: '500元',
  },
});

// 批量发送
await notificationService.sendBatch([
  { to: 'user1@example.com', ... },
  { to: 'user2@example.com', ... },
]);
```

### 2. 模板系统

**模板语法** (使用 Handlebars):

```handlebars
尊敬的 {{userName}}，

您有一个新的审批任务：

任务名称：{{taskName}}
提交人：{{submitter}}
金额：{{amount}}

请点击以下链接处理：
{{link}}

---
此邮件由系统自动发送，请勿回复。
```

**变量替换**:

```typescript
const variables = {
  userName: '张三',
  taskName: '报销审批',
  submitter: '李四',
  amount: '500元',
  link: 'https://example.com/approval/123'
};
```

### 3. 重试机制

失败自动重试策略：
- 第 1 次重试: 1 分钟后
- 第 2 次重试: 5 分钟后
- 第 3 次重试: 15 分钟后
- 3 次失败后标记为 FAILED

---

## 🚀 实施计划

### Phase 1: 基础设施 (Day 1-2)

- ✅ Prisma Schema 设计
- ✅ NotificationModule 创建
- ✅ 邮件适配器（Nodemailer）

### Phase 2: 核心功能 (Day 3-4)

- ✅ NotificationService 实现
- ✅ TemplateService 实现
- ✅ 队列处理器

### Phase 3: 高级功能 (Day 5-6)

- ✅ 重试机制
- ✅ 批量发送
- ✅ 装饰器支持

### Phase 4: 扩展和优化 (Day 7+)

- ⏳ Teams 适配器
- ⏳ 短信适配器
- ⏳ WebSocket 实时通知

---

## 📦 技术栈

| 组件 | 技术选型 | 说明 |
|------|---------|------|
| **邮件发送** | Nodemailer | 成熟稳定的 Node.js 邮件库 |
| **模板引擎** | Handlebars | 简单强大的模板语言 |
| **消息队列** | BullMQ (Redis) | 高性能消息队列 |
| **定时任务** | @nestjs/schedule | 重试和清理任务 |
| **配置管理** | @nestjs/config | 环境变量管理 |

---

## 🔧 配置示例

### 环境变量

```env
# 邮件配置
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=noreply@example.com
SMTP_PASSWORD=your_password
SMTP_FROM=FFOA System <noreply@example.com>

# Redis 配置（队列）
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=

# 通知配置
NOTIFICATION_RETRY_ENABLED=true
NOTIFICATION_MAX_RETRIES=3
NOTIFICATION_BATCH_SIZE=50
```

---

## 📊 使用场景

### 场景 1: 审批通知

```typescript
// 当审批任务创建时
@OnEvent('approval.task.created')
async handleApprovalCreated(event: ApprovalCreatedEvent) {
  await this.notificationService.sendFromTemplate({
    templateCode: 'APPROVAL_TASK_CREATED',
    to: event.approverEmail,
    variables: {
      approverName: event.approverName,
      taskName: event.taskName,
      submitter: event.submitterName,
      link: event.taskLink,
    },
  });
}
```

### 场景 2: 系统告警

```typescript
// 完整性检查失败时
if (!integrityCheckResult.success) {
  await this.notificationService.send({
    channel: 'EMAIL',
    to: 'admin@example.com',
    subject: '⚠️ 审计日志完整性检查失败',
    content: `发现 ${failures.length} 条异常记录`,
    priority: 'HIGH',
  });
}
```

### 场景 3: 批量通知

```typescript
// 周报通知
const users = await this.userService.getAllActive();
await this.notificationService.sendBatch(
  users.map(user => ({
    channel: 'EMAIL',
    to: user.email,
    templateCode: 'WEEKLY_REPORT',
    variables: {
      userName: user.displayName,
      weekStats: calculateStats(user),
    },
  }))
);
```

---

## 🎯 优势

1. **解耦**: 业务逻辑与通知逻辑分离
2. **可扩展**: 轻松添加新渠道
3. **可靠**: 自动重试 + 状态追踪
4. **高效**: 异步队列 + 批量发送
5. **易维护**: 模板化 + 统一管理

---

## 📈 性能目标

- 邮件发送: < 2 秒
- 模板渲染: < 10ms
- 队列吞吐: > 100 邮件/分钟
- 成功率: > 99%

---

准备好开始实施了吗？🚀

