Account, Profile & User Settings | 更新时间: 2026-02-08
account/membership/page.tsx, account/points/page.tsx,
account/gift-cards/page.tsx, account/gift-cards/purchase/page.tsx,
account/preferences/page.tsx,
settings/page.tsx
Settings/UserPreferencesPanel.tsx,
Settings/SettingValueInput.tsx, Settings/SettingSourceBadge.tsx,
Auth/PermissionGate.tsx, Auth/ProtectedRoute.tsx,
Loyalty/TierBadge.tsx, Loyalty/PointsBalance.tsx,
Loyalty/TierProgress.tsx, Loyalty/GiftCardBalance.tsx,
Loyalty/LoyaltyPaymentOption.tsx
contexts/AuthContext.tsx, stores/guest-auth-store.ts,
hooks/useLoyaltyApi.ts, hooks/useSettings.ts, hooks/usePermissions.ts
api/user.js (preferences, profile, switch-store, permissions),
api/guest-auth.js (register, login, OTP, profile, password, sessions)
services/guest-auth/guestAuthService.js,
services/guest-auth/guestPasswordService.js,
services/guest-auth/guestEmailService.js,
services/guest-auth/otpService.js
/settings 页面管理个人偏好(语言、默认门店、通知、日历设置)。使用 AuthContext + /api/user/*。/account/* 页面查看会员、积分、礼品卡。使用 guest-auth-store + /api/guest-auth/*。
flowchart TB
subgraph Employee["员工/管理员账户"]
EP["/settings 用户设置"]
UPP["UserPreferencesPanel"]
AC["AuthContext"]
UAPI["api/user.js"]
end
subgraph Guest["客户 (Guest) 账户"]
MEM["/account/membership"]
PTS["/account/points"]
GC["/account/gift-cards"]
GCP["/account/gift-cards/purchase"]
PREF["/account/preferences"]
GAS["guest-auth-store.ts"]
GAPI["api/guest-auth.js"]
OTP["otpService.js"]
end
subgraph Shared["共享组件"]
TB["TierBadge"]
PB["PointsBalance"]
TP["TierProgress"]
GCB["GiftCardBalance"]
end
EP --> UPP --> AC --> UAPI
MEM --> TB & PB & TP
PTS --> PB
GC --> GCB
GCP --> GAPI
MEM & PTS & GC --> GAS --> GAPI --> OTP
| 级别 | 身份 | 创建方式 | 能力 | 数据 |
|---|---|---|---|---|
| 1 | 匿名客人 | Kiosk/Walk-in(无可验证联系方式) | 仅有预约记录,不可登录 | name, source |
| 2 | 会员 | 手机号或邮箱 OTP 验证 | 登录、查看积分/会员、购买礼品卡 | phone/email, access_token/refresh_token(JWT), 可选 password_hash |
guest-auth-store.ts 使用 Zustand 管理 access_token / refresh_token / expires_at(JWT-only)。
| CUJ | 优先级 | 角色 | 描述 | 触发点 | 业务价值 | E2E 状态 |
|---|---|---|---|---|---|---|
| K1 | P0 | Guest | OTP 注册与登录 | 客户首次访问 /account | 获客 — 低门槛注册转化 | 部分覆盖 |
| K2 | P1 | Guest | 密码管理 | 注册后设密码 / 忘记密码 | 安全 — 账户保护 | 部分覆盖 |
| K3 | P1 | Guest | 会员中心 | 查看会员等级和权益 | 留存 — 会员体系可见性 | 未覆盖 |
| K4 | P1 | Guest | 积分查看与历史 | 查看积分余额和交易 | 留存 — 激励消费循环 | 未覆盖 |
| K5 | P1 | Guest | 我的礼品卡与购买 | 查看/购买礼品卡 | 营收 — 预付费 | 未覆盖 |
| K6 | P0 | Employee | 员工用户偏好设置 | /settings 页面 | 效率 — 个性化工作环境 | 部分覆盖 |
| K7 | P0 | Employee | 门店切换 | 管理多门店用户 | 效率 — 多门店管理 | 已覆盖 |
| K8 | P2 | Guest | 通知偏好设置 | /account/preferences | 合规 — opt-in/opt-out | 未覆盖 |
| K9 | P2 | Guest | Session 管理 | 查看/撤销活跃会话 | 安全 — 多设备管理 | 未覆盖 |
P0 获客 — 客户通过 OTP 验证码低门槛注册或登录
stores/guest-auth-store.ts →
api/guest-auth.js (POST /send-code, POST /verify-code, POST /login, POST /refresh) →
services/guest-auth/guestAuthService.js, services/guest-auth/otpService.js
flowchart TD
A["客户访问 /account/*"] --> B{"已登录?"}
B -->|"是"| C["进入账户页面"]
B -->|"否"| D["登录/注册页面"]
D --> E["输入手机号或邮箱"]
E --> F["POST /send-code"]
F --> G["收到 OTP 验证码"]
G --> H["输入验证码"]
H --> I["POST /verify-code"]
I --> J{"账户存在?"}
J -->|"新用户"| K["自动创建 Guest + 返回 tokens"]
J -->|"已有账户"| L["返回 tokens"]
K --> M["可选: POST /set-password"]
L --> C
M --> C
style D fill:#2196F3,stroke:#1565C0,color:#fff
style C fill:#4CAF50,stroke:#2E7D32,color:#fff
测试: guest-auth.test.js (部分)
POST /login 验证凭据测试: guest-auth.test.js
isTokenExpired() 返回 true)POST /refresh 获取新 tokensP1 安全 — 设置密码、修改密码、忘记密码重置
api/guest-auth.js (POST /set-password, POST /password-change, POST /password-reset/request, POST /password-reset/confirm) →
services/guest-auth/guestPasswordService.js, services/guest-auth/guestEmailService.js
POST /set-password 保存密码POST /password-change 验证旧密码并更新POST /password-reset/request 发送重置邮件POST /password-reset/confirm 设置新密码P1 留存 — 客户查看自己的会员等级、权益和升级进度
account/membership/page.tsx →
Loyalty/TierBadge.tsx, Loyalty/PointsBalance.tsx, Loyalty/TierProgress.tsx →
hooks/useLoyaltyApi.ts (useCustomerMembership) →
Guest Auth JWT 验证
flowchart TD
A["客户已登录 (Guest JWT)"] --> B["/account/membership"]
B --> C["加载会员信息"]
C --> D["TierBadge 等级徽章"]
C --> E["PointsBalance 积分余额"]
C --> F["TierProgress 升级进度条"]
C --> G["权益列表 (折扣%/积分倍率)"]
G --> H{"操作?"}
H -->|"查积分"| I["/account/points"]
H -->|"买礼品卡"| J["/account/gift-cards"]
style B fill:#2196F3,stroke:#1565C0,color:#fff
style D fill:#4CAF50,stroke:#2E7D32,color:#fff
| 字段 | 组件 | 说明 |
|---|---|---|
| 当前等级 | TierBadge | 等级名称 + 颜色标记 |
| 积分余额 | PointsBalance | 当前可用积分 |
| 折扣比例 | — | 当前等级享受的折扣百分比 |
| 积分倍率 | — | 消费积分倍率(如 1.5x) |
| 升级进度 | TierProgress | 当前消费 vs 下一等级门槛,进度条 |
| 当期/累计消费 | — | period_spend / lifetime_spend |
/account/membership测试: 未覆盖
/account/points/account/gift-cards
P1 留存 — 查看积分余额、交易历史、即将过期的积分
account/points/page.tsx →
Loyalty/PointsBalance.tsx →
hooks/useLoyaltyApi.ts (usePointsAccount, usePointsTransactions)
| 类型 | 图标颜色 | 说明 |
|---|---|---|
| earn | green | 消费赚取积分 |
| redeem | red | 兑换消耗积分 |
| expire | gray | 积分过期失效 |
| adjust | blue | 管理员手动调整 |
| bonus | gold | 活动奖励积分 |
/account/points测试: 未覆盖
P1 营收 — 查看持有的礼品卡、购买新礼品卡赠送他人
account/gift-cards/page.tsx, account/gift-cards/purchase/page.tsx →
Loyalty/GiftCardBalance.tsx →
hooks/useLoyaltyApi.ts (useCustomerGiftCards, usePurchaseGiftCard)
flowchart TD
A["/account/gift-cards"] --> B["礼品卡列表"]
B --> C{"操作?"}
C -->|"查看"| D["卡片详情"]
D --> E["显示/隐藏卡号 (脱敏)"]
D --> F["复制卡号"]
D --> G["余额 / 状态"]
C -->|"购买"| H["/account/gift-cards/purchase"]
H --> I["选择金额"]
I --> J["预设: $25/$50/$75/$100"]
I --> K["自定义: $10-$500"]
I --> L["选择配送方式"]
L --> M["Email / SMS / 打印"]
M --> N["填写收件人信息 + 个性化消息"]
N --> O["确认购买"]
style B fill:#2196F3,stroke:#1565C0,color:#fff
style O fill:#4CAF50,stroke:#2E7D32,color:#fff
/account/gift-cards测试: 未覆盖
/account/gift-cards/purchaseP0 效率 — 员工自定义语言、默认门店、通知和日历设置
settings/page.tsx (4 tabs) →
Settings/UserPreferencesPanel.tsx →
contexts/AuthContext.tsx (updatePreferences) →
api/user.js (GET/PUT /user/preferences, GET /user/profile)
| Tab | 内容 | 可编辑 |
|---|---|---|
| Preferences | 语言 (EN/ZH)、默认门店、默认视图 (day/week/month) | Yes |
| Profile | 姓名、邮箱、角色、所属门店 | Read-only(管理员修改) |
| Notifications | 邮件通知、SMS 通知、推送通知开关 | Yes |
| Advanced | 日历开始时间、结束时间、时间槽时长 | Yes |
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| language | enum | 'en' | 界面语言 (en/zh) |
| theme | enum | 'light' | 主题(预留) |
| default_store_id | string? | primary_store_id | 登录后默认门店 |
| default_view | enum | 'week' | 日历默认视图 |
| sidebar_collapsed | boolean | false | 侧边栏收起状态 |
| notifications.email | boolean | true | 邮件通知总开关 |
| notifications.sms | boolean | true | SMS 通知总开关 |
| notifications.push | boolean | true | 推送通知总开关 |
| calendar_settings.start_hour | number | 9 | 日历开始时间 |
| calendar_settings.end_hour | number | 21 | 日历结束时间 |
| calendar_settings.slot_duration | number | 30 | 时间槽时长(分钟) |
/settings Preferences tabPUT /user/preferences 保存到后端P0 效率 — 多门店员工在不同门店间快速切换上下文
contexts/AuthContext.tsx (switchStore, checkStoreAccess) →
api/user.js (POST /user/switch-store)
POST /user/switch-store 更新上下文测试: 已覆盖
checkStoreAccess 返回 falseP2 合规 — 客户管理通知接收偏好 (opt-in/opt-out)
account/preferences/page.tsx (⚠️ DEPRECATED)
account/preferences/page.tsx 的后端 API (/api/marketing-automation/preferences) 已移除。
页面源码注释标注为 deprecated,待迁移到 001-email-sms-integration 系统。
以下 BDD 场景描述的是目标状态。
/account/preferences测试: 未覆盖 (后端 API deprecated)
P2 安全 — 查看活跃会话、撤销异常登录
api/guest-auth.js (GET /sessions, POST /logout)
GET /sessions 返回所有活跃会话| 依赖方向 | 说明 |
|---|---|
| K → CUJ-A (Auth) | 员工 Auth(登录/JWT)在 CUJ-A;本文档覆盖登录后的账户管理 |
| K → CUJ-M (Loyalty/GiftCards) | K3/K4/K5 的会员、积分、礼品卡数据来源 |
| K → CUJ-J (Settings) | K6 UserPreferences 是 Settings 4 级体系中最底层的 User 级别 |
| K → CUJ-L (Stores) | K7 门店切换需要门店列表;K6 默认门店选择 |
| CUJ-I (Marketing) → K | K8 通知偏好影响营销邮件发送的 opt-in/opt-out |
| CUJ-G (Booking) → K | Guest 在 Booking 中创建,升级到 K1 的 member 身份 |
| # | 规则 | 实现位置 |
|---|---|---|
| 1 | 两层客户身份: 匿名客人(无可验证联系方式)与会员(有手机号/邮箱并完成 OTP)。只有 member 才能访问 /account/* 页面 | guest-auth middleware + guest-auth-store |
| 2 | OTP 优先: 060-guest-auth-redesign 后,新用户通过 send-code → verify-code 注册;旧 register 端点标记 deprecated。密码为可选(/set-password) | guest-auth.js + otpService.js |
| 3 | Token 管理: guest-auth-store 使用 Zustand 持久化到 localStorage;JWT access_token + refresh_token 双 token 机制;isTokenExpired() 检查过期 | guest-auth-store.ts |
| 4 | 密码修改撤销会话: 修改密码后 revokeAllGuestTokens 撤销所有其他会话,强制重新登录 | guestPasswordService.js |
| 5 | 礼品卡金额范围: 自定义金额 $10-$500,预设选项 $25/$50/$75/$100。配送方式: Email / SMS / 打印 | account/gift-cards/purchase/page.tsx |
| 6 | 员工偏好作用域: UserPreferences 是 Settings 4 级体系 (Platform→Tenant→Store→User) 的最底层,仅影响当前用户,不影响其他人 | settings/page.tsx + AuthContext |
| 7 | 门店切换即时生效: switchStore 更新 AuthContext.permissions.currentStore,所有数据查询自动过滤为新门店 | AuthContext.tsx + api/user.js |
| 8 | Profile 只读: 员工不能自行修改姓名/邮箱/角色,必须由管理员在 /employees/[id] 修改 | settings/page.tsx Profile tab |
| 9 | Rate Limiting: guest-auth 的 send-code / verify-code / login / register / password-reset 均有频率限制,防止暴力破解 | guest-auth.js (express-rate-limit) |