Guest/Customer Management · CRM · VIP · Tags | 更新时间: 2026-02-08
guests/page.tsx (客户列表),
guests/[id]/page.tsx (客户详情),
customers/create/page.tsx (创建客户),
admin/guests/page.tsx (管理员视图),
admin/guests/[id]/page.tsx (管理员详情),
admin/guests/duplicates/page.tsx (重复检测),
admin/guests/blacklist/page.tsx (黑名单管理)
GuestTable.tsx (可调列宽数据表),
GuestCreateForm.tsx (创建表单),
GuestEditForm.tsx (编辑表单),
AppointmentHistory.tsx (预约时间线),
ServicePreferences.tsx (服务偏好编辑),
VipBadge.tsx (VIP 徽章),
VipProgress.tsx (VIP 进度条),
GuestNotes.tsx (备注 CRUD),
GuestRelationships.tsx (关系列表),
RelationshipEditModal.tsx (关系编辑弹窗),
GuestGiftCards.tsx (礼品卡摘要),
GuestTagsList.tsx (标签筛选页),
GuestMergeWizard.tsx (合并向导),
BlacklistWarning.tsx (黑名单横幅),
AddGuestModal.tsx (预约中添加客户)
guest-auth-store.ts (Zustand 客户认证状态)
api/guests.js (14 endpoints: CRUD + VIP + blacklist + duplicates + merge + inferred-time),
api/guest-notes.js (5 endpoints: CRUD + pin),
api/guest-relationships.js (5 endpoints: CRUD + types),
api/guest-auth.js (6 endpoints: OTP + JWT),
services/guest-auth/guestAuthService.js,
services/guest-auth/otpService.js,
services/guest-auth/guestPasswordService.js,
services/guest/inferredPreferenceService.js
erDiagram
guests ||--o{ guest_notes : "has notes"
guests ||--o{ guest_tags : "has tags"
guests ||--o{ guest_relationships : "has relationships"
guests ||--o{ appointments : "has appointments"
guests ||--o{ gift_cards : "owns/receives"
guests ||--o{ guest_verification_codes : "OTP codes"
guests ||--o{ guest_merge_logs : "merge audit"
guests {
string id PK "guest_{ts}_{random}"
string name "Full name"
string phone "Optional for anonymous"
string email "Optional"
enum account_type "anonymous | identified | registered"
string password_hash "Only for registered"
enum vip_status "none | bronze | silver | gold | platinum"
int vip_points "Loyalty points"
int visit_count "Total visits"
numeric total_spent "Lifetime value"
boolean blacklist_status "Block flag"
string blacklist_reason "Admin note"
jsonb service_preferences "Allergies, prefs, comms"
string preferred_language "en | zh | es | vi | ko"
string inferred_preferred_time "AI calculated"
string source "kiosk | web_booking | staff | zenoti"
}
| 层级 | account_type | 要求 | 能力 | 来源 |
|---|---|---|---|---|
| Tier 1: 匿名客人 | anonymous |
仅姓名(可空) | Kiosk 预约、Walk-in 记录 | Kiosk(无手机号时)、Walk-in |
| Tier 2: 已识别客人 | identified |
手机号已验证 | 手机号查找、SMS 通知、Guest App 预约 | Kiosk(提供手机号)、Web Booking、Staff 创建 |
| Tier 3: 注册会员 | registered |
Email + 手机号 + 密码 | Guest App 登录、忠诚度积分、管理个人资料 | Guest App 注册、OTP 验证后设密码 |
| 等级 | 积分范围 | 图标 | 样式 |
|---|---|---|---|
| none | 0 – 99 | 隐藏 | — |
| bronze | 100 – 499 | Award | 琥珀色 |
| silver | 500 – 1,499 | Star | 石板灰 |
| gold | 1,500 – 4,999 | Crown | 金色 |
| platinum | 5,000+ | Gem | 紫色 |
| 操作 | 所需权限 | 说明 |
|---|---|---|
| 查看客户列表/详情 | guests:view | 所有员工可见 |
| 创建/编辑客户 | guests:create / guests:edit | 前台+管理员 |
| 删除客户 | guests:delete | 需检查无活跃预约 |
| VIP / 黑名单 / 合并 | guests:manage | 仅管理员 |
| CUJ | 优先级 | 描述 | 触发点 | 业务价值 | E2E 状态 |
|---|---|---|---|---|---|
| E1 | P0 | 客户列表与搜索 | 侧边栏 → Guests | CRM 基础 — 快速定位客户 | 已覆盖 |
| E2 | P0 | 客户详情 360° 视图 | 点击客户行 | 完整客户画像 — 预约/消费/备注/VIP/礼品卡 | 已覆盖 |
| E3 | P1 | 创建新客户 | Walk-in / 电话预约 | 客户数据积累,三级模型起点 | 已覆盖 |
| E4 | P1 | 客户备注管理 | 客户详情页 → 备注 Tab | 服务质量 — 技师间信息共享 | 部分覆盖 |
| E5 | P1 | 服务偏好与过敏记录 | 客户详情页 → 偏好 Tab | 个性化服务 — 过敏安全 | 部分覆盖 |
| E6 | P1 | VIP 忠诚度管理 | 客户详情页 → VIP 区域 | 客户留存 — 5 级积分体系 | 缺失 |
| E7 | P2 | 客户关系与标签 | 客户详情 → 关系/标签区域 | 家庭关联、自动化营销分群 | 部分覆盖 |
| E8 | P2 | 重复客户检测与合并 | 管理员 → 重复客户页面 | 数据质量 — 消除重复记录 | 缺失 |
| E9 | P2 | 黑名单管理 | 管理员 → 黑名单页面 | 风险控制 — 阻止问题客户预约 | 缺失 |
P0 CRM 基础 — 搜索和浏览客户
guests/page.tsx,
GuestTable.tsx,
useStoreFilter,
GET /api/guests
flowchart TD
A["侧边栏点击 Guests"] --> B["客户列表 /guests"]
B --> C{"操作"}
C -->|"搜索"| D["输入姓名/手机号 → 实时筛选"]
C -->|"筛选"| E["门店筛选 useStoreFilter + 显示非活跃"]
C -->|"点击行"| F["跳转客户详情 /guests/id"]
C -->|"添加客户"| G["打开创建表单/弹窗"]
D --> B
E --> B
style B fill:#2196F3,stroke:#1565C0,color:#fff
guests:view 权限/guests测试: customers/list.spec.ts
测试: customers/list.spec.ts
is_active: false 的客户记录
P0 完整客户画像 — 查看客户全维度信息
guests/[id]/page.tsx,
AppointmentHistory.tsx,
ServicePreferences.tsx,
VipBadge.tsx,
VipProgress.tsx,
GuestNotes.tsx,
GuestRelationships.tsx,
GuestGiftCards.tsx,
GuestEditForm.tsx,
BlacklistWarning.tsx,
GET /api/guests/{id},
GET /api/guests/{id}/history
flowchart TD
A["客户列表点击行"] --> B["客户详情页 /guests/id"]
B --> H{"黑名单?"}
H -->|"是"| BW["BlacklistWarning 横幅"]
H -->|"否"| C
B --> C["头部: 姓名 + VipBadge + 基础统计"]
C --> TAB{"Tab 切换"}
TAB --> T1["基本信息: 姓名、手机、邮箱、性别、DOB、语言"]
TAB --> T2["预约历史: AppointmentHistory 时间线"]
TAB --> T3["消费记录: 总额、平均客单价"]
TAB --> T4["备注: GuestNotes (6 类型)"]
TAB --> T5["偏好: ServicePreferences"]
TAB --> T6["礼品卡: GuestGiftCards"]
TAB --> T7["关系: GuestRelationships"]
TAB --> T8["标签: GuestTagsList"]
T1 --> ED{"编辑?"}
ED -->|"是"| EF["GuestEditForm 内联编辑"]
style B fill:#2196F3,stroke:#1565C0,color:#fff
style BW fill:#c62828,stroke:#b71c1c,color:#fff
测试: customers/details.spec.ts
guests:edit 权限PUT /api/guests/{id} 更新P1 客户数据积累 — Walk-in、电话预约、预约中添加
customers/create/page.tsx,
GuestCreateForm.tsx,
AddGuestModal.tsx,
POST /api/guests
guests:create 权限/customers/createPOST /api/guests 创建记录,ID 格式 guest_{ts}_{random}identified(有手机号)/admin/guests
测试: customers/create.spec.ts
account_type: 'identified'(有手机号)或 'anonymous'(仅姓名)。客户后续可通过 Guest App 注册升级为 'registered'。
P1 服务质量 — 技师间信息共享,私密备注保护
GuestNotes.tsx,
GET/POST/PUT/DELETE /api/guests/{id}/notes,
PUT /api/guests/{id}/notes/{noteId}/pin
| 类型 | 键值 | 用途 | 颜色 |
|---|---|---|---|
| 通用 | general | 一般性备注 | 默认 |
| 服务 | service | 服务相关(如偏好的指甲形状) | 蓝色 |
| 行为 | behavior | 客户行为特征(如常迟到) | 橙色 |
| 偏好 | preference | 个人偏好(如喜欢安静) | 绿色 |
| 医疗 | medical | 健康相关(如孕期、皮肤敏感) | 红色 |
| 其他 | other | 未分类备注 | 灰色 |
is_pinned: true)
is_private: true)P1 个性化服务 — 偏好技师、过敏安全、沟通设置
ServicePreferences.tsx,
PUT /api/guests/{id}/preferences,
guests.service_preferences (JSONB)
| 字段 | 类型 | 说明 |
|---|---|---|
| preferred_services | ID[] | 偏好服务多选 |
| preferred_employees | ID[] | 偏好技师多选 |
| allergies | string[] | 预设: Acetone, Acrylics, Gel, Latex, Fragrance, Formaldehyde + 自定义 |
| special_requests | text | 自由文本(max 500 字符) |
| preferred_time_slots | enum[] | morning (9-12) / afternoon (12-17) / evening (17-21) |
| communication_preferences | object | reminder_sms, reminder_email, marketing_sms, marketing_email |
PUT /api/guests/{id}/preferences 更新 JSONB 字段inferredPreferenceService.js 基于预约历史自动推断偏好时段 (inferred_preferred_time),支持单个刷新和批量重算。API: GET/POST /api/guests/{id}/inferred-time,POST /api/guests/inferred-time/batch。
P1 客户留存 — 5 级 VIP 体系,积分累积与等级升降
VipBadge.tsx,
VipProgress.tsx,
PUT /api/guests/{id}/vip,
guests.vip_status,
guests.vip_points
guests:manage 权限,且具备对应范围(scope)PUT /api/guests/{id}/vip 更新P2 关联客户 — 家庭/朋友关系 + 自动/手动标签分群
GuestRelationships.tsx,
RelationshipEditModal.tsx,
GuestTagsList.tsx,
api/guest-relationships.js (5 endpoints)
| 类型 | 键值 | 说明 |
|---|---|---|
| 朋友 | friend | 朋友关系 |
| 家庭 | family | 家庭成员 |
| 同事 | colleague | 工作同事 |
| 伴侣 | partner | 配偶/伴侣 |
| 其他 | other | 自定义标签补充 |
| 类别 | 来源 | 示例 |
|---|---|---|
| engagement | Auto | frequent visitor, at risk, dormant |
| birthday | Auto | 月份标签 |
| demographics | Auto | 年龄段、来源渠道 |
| purchase_history | Auto | high spender, loyal, new |
| membership | Auto | VIP 等级标签 |
| custom | Manual | 员工手动添加 |
测试: customers/relationships.spec.ts
P2 数据质量 — 检测并合并重复客户记录
admin/guests/duplicates/page.tsx,
GuestMergeWizard.tsx,
GET /api/guests/duplicates,
POST /api/guests/merge,
guest_merge_logs (审计表)
flowchart TD
A["管理员 → /admin/guests/duplicates"] --> B["系统检测重复组"]
B --> C["显示: 相似度分数 + 匹配原因(手机/邮箱)"]
C --> D{"选择操作"}
D -->|"合并"| S1["Step 1: 选择主记录"]
S1 --> S2["Step 2: 选择要合并的副本"]
S2 --> S3["确认合并(不可逆警告)"]
S3 --> R["合并结果"]
R --> R1["预约历史合并到主记录"]
R --> R2["积分累加,VIP 取最高"]
R --> R3["副本标记 inactive"]
R --> R4["guest_merge_logs 审计记录"]
D -->|"忽略"| IGN["标记为已忽略"]
style S3 fill:#c62828,stroke:#b71c1c,color:#fff
guests:manage 权限,且具备对应范围(scope)/admin/guests/duplicatesPOST /api/guests/merge 执行P2 风险控制 — 管理问题客户,阻止预约
admin/guests/blacklist/page.tsx,
BlacklistWarning.tsx,
PUT /api/guests/{id}/blacklist,
guests.blacklist_status,
guests.blacklist_reason
guests:manage 权限PUT /api/guests/{id}/blacklist 设置 blacklist_status=true/admin/guests/blacklist| 关联模块 | 关联点 | 说明 |
|---|---|---|
| CUJ-G (Public Booking) | 三级客户模型 → 预约创建 | 匿名/已识别/注册客户都可预约;黑名单客户被阻止 |
| CUJ-B (Appointments) | 客户详情 → 预约历史 | AppointmentHistory 组件显示该客户的所有预约时间线 |
| CUJ-A (Auth) | Guest Auth (OTP) | guestAuthService 管理三级升级流程: anonymous → identified → registered |
| CUJ-F (Payments) | 客户消费记录 + 礼品卡 | 客户详情页显示消费总额、客单价、礼品卡余额 |
| CUJ-I (Marketing) | 标签系统 → 营销分群 | Auto/Manual 标签用于自动化营销触发器筛选目标客户 |
| # | 规则 | 实现位置 |
|---|---|---|
| 1 | 三级客户模型: anonymous (仅姓名) → identified (手机号验证) → registered (密码保护)。Kiosk 来源跳过 SMS 验证 (source: 'kiosk' + X-Device-Type: kiosk) |
guestAuthService.js, otpService.js |
| 2 | 客户 ID 格式: guest_{timestamp}_{random},不使用 UUID |
guestAuthService.generateGuestId() |
| 3 | 合并不可逆: 副本记录标记 inactive,预约/积分转移到主记录,审计日志记录在 guest_merge_logs |
POST /api/guests/merge |
| 4 | 黑名单阻止预约: blacklist_status=true 的客户在公共预约和 Guest App 中被阻止创建新预约 | api/guests.js, 预约创建逻辑 |
| 5 | 备注隐私: is_private: true 的备注仅创建者和经理/管理员可见,普通员工的 GET 请求自动过滤 |
api/guest-notes.js |
| 6 | 偏好存储: 服务偏好、过敏、沟通偏好统一存储在 guests.service_preferences JSONB 字段 |
PUT /api/guests/{id}/preferences |
| 7 | 删除客户检查: 有活跃预约 (status ∈ pending/scheduled/confirmed/checked_in/in_progress) 的客户不可删除 | DELETE /api/guests/{id} |