Member Service
| 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
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
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
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 |