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

301 lines
9.4 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.

# 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
- 外部身份 ProvisionOIDC / LDAP / SCIM
- Profile 讀寫與業務 contact 驗證標記
- OTP challengebcrypt + 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** | 外部 IDLDAP/SCIM external_id 或 zitadel_sub→ UID 對映 |
| **UID** | 可讀主鍵,格式 `{UIDPrefix}-{Sequence}`(如 `ACME-10000003` |
| **OTP** | 一次性數字驗證碼Email/Phone 業務驗證) |
| **TOTP** | RFC 6238 時間型 OTPGoogle Authenticator |
| **Origin** | 會員來源platform_native / oidc / ldap / scim |
### 1.4 Technologies to be used
| 項目 | 技術 |
|------|------|
| Application Language | Go 1.22+ |
| Framework | go-zero |
| Cache | RedisOTP、TOTP enroll、UID seq、verify rate |
| Database | MongoDB |
| Crypto | bcryptOTP、AES-GCMTOTP 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<br/>(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<br/>(platform 註冊)
[*] --> active: Provisioning.Ensure*<br/>(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 challengeVerify 成功後立即刪除(一次性)。
**TOTP** secret 以 AES-GCM cipher 存 Mongoenroll 階段 staged secret 存 RedisTTL 600sVerifyCode 含 replay 保護timestep 去重)。
---
## 4. Data Design
### 4.1 Data Dictionary
#### tenantsMongoDB
| 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 | — |
#### membersMongoDB
| 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 | — |
#### identitiesMongoDB
| 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 challengebcrypt 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 VerificationOTP
| 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` |