template-monorepo/internal/model/notification/SDD.md

298 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Notification Service
# Notification Service — SRS/SDD Document
| Version | Version Date | Editor | Memo |
|---------|--------------|--------|------|
| 1.0.0 | 2026/05/21 | Gateway Team | 初版Gateway Notification 模組 SDD |
---
## 1. Introduction
### 1.1 Purpose
Notification 模組為 TianTing Gateway 的**統一對外通知入口**,支援 Email 與 SMS 的同步寄送、異步佇列、冪等、租戶配額、指數退避重試與 DLQDead Letter Queue管理。
### 1.2 Scope
**範圍內:**
- 同步 `Send`:渲染模板後立即送達
- 異步 `Enqueue`Mongo 寫入 pending + Redis ZSET 排程 + RetryWorker 背景投遞
- 冪等Redis cache + Mongo unique index
- 租戶每日 Email/SMS 配額
- 模板渲染embed HTML / SMS / Subject多語系
- Provider chainSMTP、SES、Mitake、Mock
- Admin DLQ 查詢與手動重試
**範圍外:**
- Push / Webhookenum 已定義,尚未實作)
- 專用 HTTP API目前僅內部 logic 呼叫;擴充路徑見 README
### 1.3 Definitions, Acronyms, and Abbreviation
| 縮寫 | 說明 |
|------|------|
| **NotifyKind** | 通知類型verify_email、tenant_welcome 等) |
| **Channel** | 通道email / sms / push / webhook |
| **DLQ** | Dead Letter Queue超過 MaxRetry 的失敗通知 |
| **IdempotencyKey** | 冪等鍵,同 tenant+kind+key 不重複寄送 |
| **TargetHash** | SHA-256(target)Mongo 不存明文 email/phone |
| **RetryJob** | Redis ZSET 成員,含 target僅 Redis不落 Mongo |
### 1.4 Technologies to be used
| 項目 | 技術 |
|------|------|
| Application Language | Go 1.22+ |
| Framework | go-zero |
| Database | MongoDBnotifications、notification_dlq |
| Cache / Queue | Redis冪等、配額、retry ZSET |
| Templates | go:embed + Go text/html template |
| Email Providers | SMTP、AWS SES、Mock |
| SMS Providers | Mitake三竹、Mock |
### 1.5 Overview
Notification 為 library-style domain package嵌入 Gateway process 內執行:
1. **Send**Caller → 冪等檢查 → 配額 → Insert pending → Render → Provider chain → Update sent/failed
2. **Enqueue**:同上至 Insert → ZADD Redis → 回傳 pendingRetryWorker 背景 ClaimDue → 投遞
3. **DLQ**attempts ≥ MaxRetry → status=dropped + 寫入 notification_dlq
---
## 2. System Overview
Notification 模組解耦業務邏輯與實際寄送:
- Auth 註冊 OTP、Member 業務驗證等 logic 層呼叫 `Notifier.Send`
- 歡迎信等可容忍延遲的通知可走 `Enqueue` + Worker
- 隱私設計Mongo / DLQ 只存 `target_hash`,明文 target 僅在 Redis RetryJob 生命週期內存在
Worker 由 `ServiceContext.StartWorkers` 啟動,與 Gateway 同 process 運行。
---
## 3. System Architecture
### 3.1 System Architecture
```mermaid
flowchart TB
subgraph Callers["Internal Callers"]
AuthLogic[logic/auth]
MemberLogic[logic/member]
end
subgraph NotificationModule["internal/model/notification"]
Notifier[NotifierUseCase]
Admin[AdminNotifierUseCase]
Worker[RetryWorker]
Factory[Provider Factory]
Renderer[Template Renderer]
end
subgraph Providers["Providers"]
EmailChain[email.Chain<br/>SMTP / SES / Mock]
SMSChain[sms.Chain<br/>Mitake / Mock]
end
subgraph Storage["Storage"]
Mongo[(MongoDB)]
Redis[(Redis)]
end
AuthLogic --> Notifier
MemberLogic --> Notifier
Notifier --> Renderer
Notifier --> Factory
Notifier --> Mongo
Notifier --> Redis
Factory --> EmailChain
Factory --> SMSChain
Worker --> Redis
Worker --> Mongo
Worker --> EmailChain
Worker --> SMSChain
Admin --> Mongo
Admin --> Redis
```
### 3.2 Decomposition Description
| 套件 | 職責 |
|------|------|
| `config/` | Async、Rate、Email、SMS 設定 |
| `domain/entity/` | Notification、NotificationDLQ |
| `domain/enum/` | Channel、NotifyKind、NotifyStatus、Severity |
| `domain/repository/` | Notification、DLQ、Store、RetryQueue 介面 |
| `domain/usecase/` | Notifier、Admin 介面 + DTO |
| `domain/template/` | 模板 Spec、Renderer、Registry 介面 |
| `repository/` | Mongo + Redis 實作 |
| `usecase/` | Notifier、Admin、RetryWorker、factory、module |
| `provider/email/` | SMTP、SES、Mock sender + chain |
| `provider/sms/` | Mitake、Mock sender + chain |
| `template/` | embed 模板 + render |
### 3.3 Send同步
```mermaid
sequenceDiagram
participant C as Caller
participant N as NotifierUseCase
participant R as Redis
participant M as Mongo
participant P as Provider
C->>N: Send(SendRequest)
N->>R: Get idempotency
alt cache hit
N-->>C: cached DTO
else miss
N->>M: FindByIdempotency / Insert pending
N->>R: Incr quota
N->>N: Render template
N->>P: Email/SMS chain.Send
N->>M: UpdateDelivery (sent/failed)
N->>R: Set idempotency cache
N-->>C: NotificationDTO
end
```
### 3.4 Enqueue + RetryWorker異步
```mermaid
sequenceDiagram
participant C as Caller
participant N as NotifierUseCase
participant M as Mongo
participant R as Redis
participant W as RetryWorker
participant P as Provider
C->>N: Enqueue(SendRequest)
N->>M: Insert (pending)
N->>R: ZADD RetryJob (score=now)
N-->>C: pending DTO
loop every 1s
W->>R: ClaimDue (ZREM)
W->>M: FindByID
W->>P: Render + deliver
alt success
W->>M: status=sent
else attempts < MaxRetry
W->>M: status=retrying
W->>R: ZADD (score=now+backoff)
else
W->>M: status=dropped
W->>M: Insert DLQ
end
end
```
**退避序列(預設):** `[1, 5, 30, 300, 1800]`
---
## 4. Data Design
### 4.1 Data Dictionary
#### notificationsMongoDB
| Field | Type | Comment | Index |
|-------|------|---------|-------|
| _id | ObjectId | PK | PK |
| tenant_id | String | 租戶 ID | Unique(tenant_id, kind, idempotency_key) |
| uid | String | 會員 UID | (tenant_id, uid, occurred_at desc) |
| channel | String | email / sms | — |
| kind | String | NotifyKind | Unique(tenant_id, kind, idempotency_key) |
| target_hash | String | SHA-256(target) | — |
| template_key | String | 模板 key | — |
| locale | String | 語系 | — |
| body | String | 渲染後內容OTP 類可省略) | — |
| provider | String | 實際使用的 provider | — |
| provider_message_id | String | 外部 message ID | — |
| status | String | pending/sent/failed/retrying/dropped | (status, attempts, occurred_at) |
| attempts | Int | 投遞嘗試次數 | — |
| last_error | String | 最後錯誤 | — |
| idempotency_key | String | 冪等鍵 | Unique(tenant_id, kind, idempotency_key) |
| severity | String | info/warn/critical | — |
| occurred_at | Int64 | 事件時間 ms | — |
| delivered_at | Int64 | 送達時間 ms | — |
| create_at | Int64 | 建立時間 ms | — |
| update_at | Int64 | 更新時間 ms | — |
#### notification_dlqMongoDB
| Field | Type | Comment | Index |
|-------|------|---------|-------|
| _id | ObjectId | PK | PK |
| notification_id | String | 原 notification ID | — |
| tenant_id | String | 租戶 ID | — |
| uid | String | 會員 UID | — |
| channel | String | 通道 | — |
| kind | String | 通知類型 | — |
| target_hash | String | target hash | — |
| last_error | String | 最後錯誤 | — |
| attempts | Int | 總嘗試次數 | — |
| payload | Object | 重試 metadatalocale、data 等,不含 target | — |
| occurred_at | Int64 | 事件時間 ms | — |
| create_at | Int64 | 建立時間 ms | — |
#### Redis Keys
| Key Pattern | 用途 | TTL |
|-------------|------|-----|
| notif:idem:{tenant}:{kind}:{key} | 冪等 cache | 24h |
| notif:quota:{tenant}:{channel}:{yyyyMMdd} | 每日配額計數 | 25h |
| notif:retry:zset可配置 | RetryJob ZSETscore=run_at_ms | — |
#### NotifyKind 一覽
| Kind | 通道 | 說明 |
|------|------|------|
| verify_email | email | 業務 email OTP |
| verify_phone | sms | 業務 phone OTP |
| verify_registration_email | email | 註冊 email OTP |
| step_up_email | email | Email step-up |
| step_up_phone | sms | Phone step-up |
| account_suspended | email | 帳號停權通知 |
| tenant_welcome | email | 租戶歡迎信 |
---
## 5. API Design
**現況:** 尚無專用 HTTP API。Notification 由內部 logic 呼叫:
| 呼叫方 | 方法 | 用途 |
|--------|------|------|
| `logic/auth/register_logic.go` | `Notifier.Send` | 註冊 email OTP |
| `logic/member/verify_helper.go` | `Notifier.Send` | 業務 email/phone OTP |
| `cmd/notify-test` | Send / Enqueue / Admin | 本機測試 CLI |
**擴充 HTTP API 路徑:**
1.`generate/api/` 定義路由
2. `make gen-api`
3. `internal/logic` 映射 types ↔ DTO
---
## 6. Resource
| 資源 | 路徑 |
|------|------|
| 模組原始碼 | `internal/model/notification/` |
| 開發 README | `internal/model/notification/README.md` |
| 測試指南 | `docs/notification-testing.md` |
| 測試 CLI | `cmd/notify-test``make notify-test` |
| Worker | `internal/worker/notification_retry/` |
| 設定範例 | `etc/gateway.dev.example.yaml``Notification` 區塊 |
| 設計參考 | `docs/identity-member-design.md` §11 |