# Member Service
# Member Service — SRS/SDD Document
| Version | Version Date | Editor | Memo |
|---------|--------------|--------|------|
| 1.0.0 | 2026/05/21 | Gateway Team | 初版:Gateway Member 模組 SDD |
---
## 1. Introduction
### 1.1 Purpose
Member 模組為 TianTing Gateway 的**會員核心領域層**,管理多租戶下的 Tenant(租戶)、Member(會員 profile)、Identity(外部身份對映),以及 UID 生成、業務 Email/Phone OTP 驗證、TOTP step-up MFA、重發/每日配額等原子業務原語。
### 1.2 Scope
**範圍內:**
- 租戶建立與 slug 解析
- 會員生命週期(unverified → active → suspended → deleted)
- 外部身份 Provision(OIDC / LDAP / SCIM)
- Profile 讀寫與業務 contact 驗證標記
- OTP challenge(bcrypt + Redis)
- TOTP 綁定/驗證/備援碼(AES-GCM 保護 secret)
- 可讀 UID 序號生成
**範圍外:**
- 密碼驗證、JWT 簽發 → Auth 模組 + ZITADEL
- 通知寄送 → Notification 模組(logic 層編排)
- RBAC → Permission 模組
**架構原則:** usecase **不可**呼叫其他 usecase;多步流程由 logic 層編排。
### 1.3 Definitions, Acronyms, and Abbreviation
| 縮寫 | 說明 |
|------|------|
| **Tenant** | 租戶,B2B 客戶組織單位 |
| **Member** | 租戶範圍內的會員 profile |
| **Identity** | 外部 ID(LDAP/SCIM external_id 或 zitadel_sub)→ UID 對映 |
| **UID** | 可讀主鍵,格式 `{UIDPrefix}-{Sequence}`(如 `ACME-10000003`) |
| **OTP** | 一次性數字驗證碼(Email/Phone 業務驗證) |
| **TOTP** | RFC 6238 時間型 OTP(Google Authenticator) |
| **Origin** | 會員來源:platform_native / oidc / ldap / scim |
### 1.4 Technologies to be used
| 項目 | 技術 |
|------|------|
| Application Language | Go 1.22+ |
| Framework | go-zero |
| Cache | Redis(OTP、TOTP enroll、UID seq、verify rate) |
| Database | MongoDB |
| Crypto | bcrypt(OTP)、AES-GCM(TOTP secret)、RFC 6238 TOTP |
| API Codegen | goctl / `.api` 檔 |
### 1.5 Overview
Member 模組提供 7 個 atomic UseCase,由 `NewModuleFromParam` 依 Mongo / Redis / TOTP KEK 條件組裝:
| UseCase | 條件 |
|---------|------|
| OTP、VerifyRate | Redis 必填 |
| Profile、Lifecycle、Tenant、Provisioning | Mongo 設定後 |
| TOTP | Mongo + `TOTP.SecretKEK` 設定後 |
---
## 2. System Overview
Member 模組是多租戶身份系統的資料核心:
- 每個 Member 必屬一個 Tenant
- UID 以 `{TenantUIDPrefix}-{Sequence}` 生成,序號從 10,000,000 起跳
- 平台註冊走 `unverified → active`;OIDC/LDAP/SCIM 首登直接 `active`
- 業務 Email/Phone 驗證與 TOTP 為 step-up MFA 能力
對外透過 `/api/v1/members/*` REST API 暴露(profile、verification、TOTP)。
---
## 3. System Architecture
### 3.1 System Architecture
```mermaid
flowchart TB
subgraph Client["Client"]
App[Frontend / API Client]
end
subgraph Gateway["Gateway"]
Handler["handler/member"]
Logic["logic/member
(orchestration)"]
subgraph MemberModule["internal/model/member"]
UC["usecase/ (7 atomic)"]
Repo["repository/"]
TOTP["totp/ (RFC 6238)"]
end
end
subgraph Storage["Storage"]
Mongo[(MongoDB)]
Redis[(Redis)]
end
subgraph Sibling["Sibling Modules"]
Auth[auth]
Notif[notification]
end
App --> Handler
Handler --> Logic
Logic --> UC
Logic --> Notif
Auth --> UC
UC --> Repo
UC --> TOTP
Repo --> Mongo
Repo --> Redis
```
### 3.2 Decomposition Description
| 套件 | 職責 |
|------|------|
| `config/` | OTP / TOTP / Registration 設定 |
| `domain/entity/` | Member、Tenant、Identity Mongo doc |
| `domain/enum/` | MemberStatus、MemberOrigin、OTPPurpose、TenantStatus |
| `domain/repository/` | 7 個 repository 介面 |
| `domain/usecase/` | 7 個 usecase 介面 + DTO |
| `domain/redis.go` | Redis key helper(禁止字串拼接) |
| `repository/` | Mongo + Redis 實作 |
| `totp/` | RFC 6238 純函式(secret、verify、otpauth URL) |
| `usecase/` | 實作 + module factory + mapper |
### 3.3 Member Lifecycle
```mermaid
stateDiagram-v2
[*] --> unverified: Lifecycle.CreateUnverified
(platform 註冊)
[*] --> active: Provisioning.Ensure*
(OIDC/LDAP/SCIM)
unverified --> active: Activate (OTP 通過)
unverified --> deleted: AbortPending (註冊逾時)
active --> suspended: Suspend
suspended --> active: Reactivate
active --> deleted: SoftDelete
suspended --> deleted: SoftDelete
deleted --> [*]
```
### 3.4 UID Generation
```mermaid
sequenceDiagram
participant UC as Lifecycle / Provisioning
participant Gen as UIDGenerator
participant R as Redis
UC->>Gen: Next(tenant, uidPrefix)
Gen->>R: INCR member:seq:{tenant}
alt seq == 1 (首次)
Gen->>R: INCRBY (UIDSequenceStart - 1)
end
Gen-->>UC: "{PREFIX}-{seq}"
```
- `UIDSequenceStart = 10_000_000`
- `UIDPrefix` 限制 2~4 個大寫字母
### 3.5 OTP / TOTP
**OTP:** bcrypt hash 存 Redis challenge;Verify 成功後立即刪除(一次性)。
**TOTP:** secret 以 AES-GCM cipher 存 Mongo;enroll 階段 staged secret 存 Redis(TTL 600s);VerifyCode 含 replay 保護(timestep 去重)。
---
## 4. Data Design
### 4.1 Data Dictionary
#### tenants(MongoDB)
| Field | Type | Comment | Index |
|-------|------|---------|-------|
| _id | ObjectId | PK | PK |
| tenant_id | String | 租戶 ID | — |
| slug | String | URL slug | Unique |
| name | String | 顯示名稱 | — |
| uid_prefix | String | UID 前綴(2-4 大寫) | Unique |
| status | String | active / suspended | — |
| org_id | String | 外部 org ID | — |
| create_at | Int64 | 建立時間 ms | — |
| update_at | Int64 | 更新時間 ms | — |
#### members(MongoDB)
| Field | Type | Comment | Index |
|-------|------|---------|-------|
| _id | ObjectId | PK | PK |
| tenant_id | String | 租戶 ID | Unique(tenant_id, uid) |
| uid | String | 可讀 UID | Unique(tenant_id, uid) |
| zitadel_user_id | String | ZITADEL sub | Unique(tenant_id, zitadel_user_id) sparse |
| zitadel_email | String | IdP email | — |
| display_name | String | 顯示名稱 | — |
| avatar | String | 頭像 URL | — |
| phone | String | 聯絡電話 | — |
| language | String | 語系 | — |
| currency | String | 幣別 | — |
| member_status | String | unverified/active/suspended/deleted | — |
| origin | String | platform_native/oidc/ldap/scim | — |
| business_email | String | 業務 email | — |
| business_email_verified | Bool | 業務 email 已驗證 | — |
| business_phone | String | 業務 phone | — |
| business_phone_verified | Bool | 業務 phone 已驗證 | — |
| totp_enrolled | Bool | TOTP 已綁定 | — |
| totp_secret_cipher | Binary | AES-GCM 加密 secret | — |
| totp_backup_codes_hash | Array | bcrypt 備援碼 hash | — |
| suspend_reason | String | 停權原因 | — |
| create_at | Int64 | 建立時間 ms | — |
| update_at | Int64 | 更新時間 ms | — |
| deleted_at | Int64 | 軟刪時間 ms | — |
#### identities(MongoDB)
| Field | Type | Comment | Index |
|-------|------|---------|-------|
| _id | ObjectId | PK | PK |
| tenant_id | String | 租戶 ID | Unique(tenant_id, external_id) |
| external_id | String | LDAP/SCIM 外部 ID | Unique(tenant_id, external_id) |
| zitadel_user_id | String | ZITADEL sub | Unique(tenant_id, zitadel_user_id) |
| uid | String | 對應 Member UID | — |
| create_at | Int64 | 建立時間 ms | — |
#### Redis Keys
| Key Pattern | 用途 | TTL |
|-------------|------|-----|
| member:otp:challenge:{id} | OTP challenge(bcrypt hash) | 300s |
| member:otp:challenge:{id}:attempts | OTP 錯誤次數 | 同 challenge |
| member:verify:rate:{tenant}:{uid}:{kind} | 重發冷卻 | 60s |
| member:verify:daily:{tenant}:{uid}:{kind} | 每日上限計數 | 24h |
| member:totp:enroll:{tenant}:{uid} | TOTP 綁定 staged secret | 600s |
| member:totp:used:{tenant}:{uid}:{step} | TOTP 重放保護 | 90s |
| member:seq:{tenant} | UID 序號 INCR | 永久 |
---
## 5. API Design
**Base Path:** `/api/v1/members`
### 5.1 Profile
| Method | Path | 說明 |
|--------|------|------|
| GET | `/me` | 取得當前會員 profile |
| PATCH | `/me` | 更新 display_name / avatar / language 等 |
### 5.2 Verification(OTP)
| Method | Path | 說明 |
|--------|------|------|
| POST | `/me/verifications/email/start` | 發起業務 email OTP |
| POST | `/me/verifications/email/confirm` | 確認 email OTP |
| POST | `/me/verifications/phone/start` | 發起業務 phone OTP |
| POST | `/me/verifications/phone/confirm` | 確認 phone OTP |
### 5.3 TOTP
| Method | Path | 說明 |
|--------|------|------|
| GET | `/me/totp/status` | TOTP 綁定狀態 |
| POST | `/me/totp/enroll` | 開始綁定(回 otpauth_url) |
| POST | `/me/totp/enroll/confirm` | 確認綁定(回 backup codes) |
| POST | `/me/totp/verify` | Step-up 驗碼 |
| POST | `/me/totp/disable` | 解除綁定 |
| POST | `/me/totp/backup-codes/regenerate` | 重產備援碼 |
完整 schema 見 `generate/api/member.api`。
---
## 6. Resource
| 資源 | 路徑 |
|------|------|
| API 定義 | `generate/api/member.api` |
| Logic 編排 | `internal/logic/member/` |
| 模組原始碼 | `internal/model/member/` |
| 開發 README | `internal/model/member/README.md` |
| 設定範例 | `etc/gateway.dev.example.yaml` → `Member` 區塊 |
| 設計參考 | `docs/identity-member-design.md`、`docs/model.md` §6.1 |
| Seed CLI | `cmd/member-seed` |