# 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 的同步寄送、異步佇列、冪等、租戶配額、指數退避重試與 DLQ(Dead Letter Queue)管理。 ### 1.2 Scope **範圍內:** - 同步 `Send`:渲染模板後立即送達 - 異步 `Enqueue`:Mongo 寫入 pending + Redis ZSET 排程 + RetryWorker 背景投遞 - 冪等(Redis cache + Mongo unique index) - 租戶每日 Email/SMS 配額 - 模板渲染(embed HTML / SMS / Subject,多語系) - Provider chain(SMTP、SES、Mitake、Mock) - Admin DLQ 查詢與手動重試 **範圍外:** - Push / Webhook(enum 已定義,尚未實作) - 專用 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 | MongoDB(notifications、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 → 回傳 pending;RetryWorker 背景 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
SMTP / SES / Mock] SMSChain[sms.Chain
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 #### notifications(MongoDB) | 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_dlq(MongoDB) | 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 | 重試 metadata(locale、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 ZSET(score=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 |