Celoria 系统架构图
员工端 (Web) + 顾客端 (Mobile) 完整流程
一、三层架构总览
员工端 (Web) 和顾客端 (Mobile) 共享同一后端,但使用不同的认证方式
graph TB
subgraph StaffWeb["🖥️ 员工端 Web (Next.js 14 + TypeScript)"]
F1["📄 登录页面
login/page.tsx"]
F2["🔐 AuthContext
contexts/AuthContext.tsx"]
F3["📅 预约页面
appointments/page.tsx"]
F4["📋 预约看板
AppointmentBoard.tsx"]
end
subgraph GuestMobile["📱 顾客端 Mobile Web (034/035)"]
M1["📝 注册/登录
/booking/register
/booking/login"]
M2["🔐 GuestAuthContext
guest JWT / guest_token"]
M3["📅 在线预约
/booking/appointments"]
M4["⭐ 会员中心
/booking/account
积分/礼品卡/等级"]
end
subgraph Backend["⚙️ 后端层 Backend (Express.js + Node.js)"]
subgraph Auth["认证模块"]
B1["🔑 auth-controller.js
员工登录"]
B1G["🔑 guest-auth.js
顾客注册/登录"]
B2["🎫 jwt-service.js
Token生成/验证"]
end
subgraph MW["中间件"]
B3["🛡️ auth-middleware.js
员工JWT验证"]
B3G["🛡️ guest-jwt-auth.js
顾客JWT验证"]
B4["🔒 permission-middleware.js
RBAC权限检查"]
end
subgraph API["业务API"]
B5["📅 appointments.js
预约CRUD(员工)"]
B5G["📅 public/booking.js
预约CRUD(顾客)"]
B6["👤 employees.js
员工管理"]
B6G["⭐ loyalty.js
会员体系"]
end
subgraph Services["服务层"]
B7["⏰ availability-calculator.js"]
B8["⚠️ conflict-detector.js"]
B9["📧 notification-dispatcher.js"]
end
end
subgraph Database["🗄️ 数据库层 Database (PostgreSQL 15)"]
subgraph UserTables["用户相关表"]
D1[("guests
客户表")]
D2[("employees
员工表")]
D3[("auth_sessions
员工会话")]
D3G[("guest_auth_sessions
顾客会话")]
end
subgraph AppTables["预约相关表"]
D4[("appointments
主预约表")]
D5[("sub_appointments
子预约表")]
end
subgraph LoyaltyTables["会员体系表"]
D9[("points_accounts
积分账户")]
D10[("gift_cards
礼品卡")]
D11[("membership_tiers
会员等级")]
end
end
%% 员工端流程
F1 --> F2
F2 -->|"POST /api/auth/login"| B1
F3 --> F4
F4 -->|"GET/POST /api/appointments"| B3
%% 顾客端流程
M1 --> M2
M2 -->|"POST /api/guest-auth/*"| B1G
M3 -->|"GET/POST /api/public/booking/*"| B3G
M4 -->|"GET /api/loyalty/*"| B3G
%% 后端内部
B1 --> B2
B1G --> B2
B2 --> D2
B2 --> D1
B1 --> D3
B1G --> D3G
B3 --> B4
B4 --> B5
B3G --> B5G
B3G --> B6G
B5 --> B7
B5 --> B8
B5G --> B7
B5G --> B8
B5 --> B9
B5G --> B9
B5 --> D4
B5 --> D5
B5G --> D4
B5G --> D5
B6G --> D9
B6G --> D10
B6G --> D11
style StaffWeb fill:#0d4f4a,stroke:#4ecdc4,stroke-width:2px
style GuestMobile fill:#4a3d0d,stroke:#f9a825,stroke-width:2px
style Backend fill:#4a1a1a,stroke:#ff6b6b,stroke-width:2px
style Database fill:#4a4a1a,stroke:#ffd93d,stroke-width:2px
style Auth fill:#3d1a4a,stroke:#a855f7
style MW fill:#3d1a4a,stroke:#a855f7
二、员工端登录流程 (Web)
员工使用邮箱+密码登录,获取 JWT token,用于访问后台管理功能
sequenceDiagram
autonumber
participant U as 👤 员工
participant FE as 🖥️ 员工端 Web
AuthContext.tsx
participant AC as 🔑 后端
auth-controller.js
participant JWT as 🎫 JWT服务
jwt-service.js
participant DB as 🗄️ 数据库
PostgreSQL
U->>FE: 输入邮箱/密码
FE->>AC: POST /api/auth/login
AC->>DB: 查询 employees 表
DB-->>AC: 返回员工记录
AC->>AC: bcrypt 验证密码
AC->>AC: 检查账户状态(锁定/禁用)
AC->>JWT: 生成 access_token
JWT-->>AC: RS256签名的JWT (15分钟)
AC->>JWT: 生成 refresh_token
JWT-->>AC: refresh_token (7天)
AC->>DB: 创建 auth_sessions 记录
AC-->>FE: {user, access_token, refresh_token}
FE->>FE: localStorage 存储 token
FE->>FE: 设置自动刷新定时器
FE-->>U: 登录成功,跳转仪表板
三、顾客端登录流程 (Mobile)
顾客有两种认证方式:① 注册会员用邮箱+密码登录(JWT);② 匿名预约用手机验证码(guest_token)
sequenceDiagram
autonumber
participant G as 👤 顾客
participant MW as 📱 Mobile Web
GuestAuthContext
participant GA as 🔑 后端
guest-auth.js
participant JWT as 🎫 JWT服务
jwt-service.js
participant DB as 🗄️ 数据库
PostgreSQL
participant EMAIL as 📧 邮件服务
rect rgb(40, 60, 40)
Note over G,EMAIL: 方式一:注册会员登录 (JWT)
G->>MW: 输入邮箱/密码
MW->>GA: POST /api/guest-auth/login
GA->>DB: 查询 guests 表 (account_type='registered')
DB-->>GA: 返回顾客记录
GA->>GA: bcrypt 验证密码
GA->>GA: 检查账户锁定 (5次失败锁15分钟)
GA->>JWT: 生成 access_token (30分钟)
JWT-->>GA: RS256签名的JWT
GA->>JWT: 生成 refresh_token (30天)
JWT-->>GA: refresh_token
GA->>DB: 创建 guest_auth_sessions
GA-->>MW: {guest, tokens, membership_tier, points_balance}
MW->>MW: 存储 JWT token
MW-->>G: 登录成功,可访问会员中心
end
rect rgb(60, 50, 30)
Note over G,EMAIL: 方式二:匿名预约 (guest_token)
G->>MW: 输入手机号
MW->>GA: POST /api/guest-auth/send-code
GA->>DB: 创建/查找 Guest 记录 (account_type='anonymous')
GA-->>MW: 验证码已发送
G->>MW: 输入验证码
MW->>GA: POST /api/guest-auth/verify-code
GA->>GA: 验证码校验
GA->>DB: 创建 guest_token (7天有效)
GA-->>MW: {guest_token, guest_info}
MW->>MW: 存储 guest_token
MW-->>G: 验证成功,可以预约
end
四、顾客注册流程 (Mobile)
新顾客注册为会员,解锁积分、礼品卡、会员等级等功能
sequenceDiagram
autonumber
participant G as 👤 顾客
participant MW as 📱 Mobile Web
/booking/register
participant GA as 🔑 后端
guest-auth.js
participant JWT as 🎫 JWT服务
participant DB as 🗄️ 数据库
participant LOYALTY as ⭐ 会员服务
participant EMAIL as 📧 邮件服务
G->>MW: 填写注册表单
(邮箱、密码、姓名、手机)
MW->>MW: 前端验证
(邮箱格式、密码强度)
MW->>GA: POST /api/guest-auth/register
GA->>DB: 检查邮箱唯一性
alt 邮箱已存在
GA-->>MW: 400 EMAIL_ALREADY_EXISTS
MW-->>G: 显示"该邮箱已被注册"
else 邮箱可用
GA->>GA: bcrypt 哈希密码 (12 rounds)
GA->>DB: INSERT guests (account_type='registered')
GA->>LOYALTY: 创建积分账户 (初始0分)
LOYALTY->>DB: INSERT points_accounts
GA->>JWT: 生成 JWT tokens
GA->>EMAIL: 发送邮箱验证邮件
GA->>DB: INSERT guest_email_verifications
GA-->>MW: {guest, tokens, message}
MW->>MW: 存储 JWT token
MW-->>G: 注册成功!请验证邮箱
end
五、员工端创建预约流程 (Web)
员工通过后台系统为顾客创建预约,需要 RBAC 权限验证
sequenceDiagram
autonumber
participant U as 👤 员工
participant FE as 🖥️ 员工端 Web
预约看板
participant AM as 🛡️ auth-middleware
participant PM as 🔒 permission-middleware
participant API as 📅 appointments.js
participant SVC as ⚙️ 服务层
participant DB as 🗄️ 数据库
U->>FE: 填写预约表单
FE->>AM: POST /api/appointments
Header: Bearer token
AM->>AM: 验证员工JWT签名
AM->>DB: 检查员工状态
AM->>PM: req.user 附加完成
PM->>DB: 查询 role_permissions
PM->>DB: 查询 job_title_permissions
PM->>DB: 查询 special_permissions
PM->>PM: 计算最终权限
DENY > GRANT > Role
PM->>API: 权限验证通过 (appointments:create)
API->>SVC: conflict-detector 检查冲突
SVC-->>API: 无冲突
API->>SVC: availability-calculator 检查可用性
SVC-->>API: 员工可用
API->>DB: INSERT appointments
API->>DB: INSERT sub_appointments
API->>SVC: notification-dispatcher
SVC-->>API: 通知已发送
API-->>FE: {success: true, data: appointment}
FE-->>U: 显示预约成功
六、顾客端在线预约流程 (Mobile)
顾客通过 Mobile Web 自助预约,支持 JWT 或 guest_token 认证
sequenceDiagram
autonumber
participant G as 👤 顾客
participant MW as 📱 Mobile Web
/booking/appointments
participant GA as 🛡️ guest-jwt-auth
participant API as 📅 public/booking.js
participant SVC as ⚙️ 服务层
participant DB as 🗄️ 数据库
participant NOTIFY as 📧 通知服务
G->>MW: 选择服务/技师/时间
MW->>MW: 显示可用时段
G->>MW: 确认预约信息
MW->>GA: POST /api/public/booking/appointments
Header: Bearer token 或 guest_token
alt JWT 认证 (注册会员)
GA->>GA: 验证 JWT (user_type='guest')
GA->>DB: 查询 guests (account_type='registered')
else guest_token 认证 (匿名预约)
GA->>GA: 验证 guest_token (7天有效)
GA->>DB: 查询 guests (account_type='anonymous')
end
GA->>API: req.guest 附加完成
API->>SVC: availability-calculator 检查可用性
SVC-->>API: 时段可用
API->>SVC: conflict-detector 检查冲突
SVC-->>API: 无冲突
API->>DB: INSERT appointments (source='online')
API->>DB: INSERT sub_appointments
API->>NOTIFY: 发送确认短信/邮件
NOTIFY-->>API: 通知已发送
API-->>MW: {success: true, data: appointment}
MW-->>G: 预约成功!显示确认信息
七、代码文件对照表
员工端和顾客端的前后端代码对应关系
graph LR
subgraph 员工端Web["员工端 Web 文件"]
A1["contexts/AuthContext.tsx
员工认证状态"]
A2["app/.../appointments/page.tsx
预约管理页"]
A3["components/Appointment/Board/
AppointmentBoard.tsx"]
end
subgraph 顾客端Mobile["顾客端 Mobile 文件"]
M1["contexts/GuestAuthContext.tsx
顾客认证状态"]
M2["app/booking/login/page.tsx
顾客登录页"]
M3["app/booking/register/page.tsx
顾客注册页"]
M4["app/booking/appointments/page.tsx
在线预约页"]
M5["app/booking/account/page.tsx
会员中心"]
end
subgraph 后端认证["后端 - 认证模块"]
B1["auth/auth-controller.js
员工登录API"]
B1G["api/guest-auth.js
顾客认证API"]
B2["auth/jwt-service.js
JWT令牌服务"]
B3["auth/auth-middleware.js
员工JWT中间件"]
B3G["middleware/guest-jwt-auth.js
顾客JWT中间件"]
end
subgraph 后端业务["后端 - 业务模块"]
B5["api/appointments.js
预约API(员工)"]
B5G["api/public/booking.js
预约API(顾客)"]
B6G["api/loyalty.js
会员体系API"]
B7["services/booking/*
预约服务层"]
B8["services/guest-auth/*
顾客认证服务"]
end
subgraph 数据库表
C1["employees
员工表"]
C1G["guests
顾客表"]
C2["auth_sessions
员工会话"]
C2G["guest_auth_sessions
顾客会话"]
C3["appointments
sub_appointments"]
C4G["points_accounts
gift_cards
membership_tiers"]
end
A1 -->|"调用"| B1
M1 -->|"调用"| B1G
M2 -->|"调用"| B1G
M3 -->|"调用"| B1G
A2 -->|"调用"| B5
A3 -->|"调用"| B5
M4 -->|"调用"| B5G
M5 -->|"调用"| B6G
B1 -->|"使用"| B2
B1G -->|"使用"| B2
B5 -->|"经过"| B3
B5G -->|"经过"| B3G
B6G -->|"经过"| B3G
B5 -->|"使用"| B7
B5G -->|"使用"| B7
B1G -->|"使用"| B8
B1 -->|"读写"| C1
B1G -->|"读写"| C1G
B1 -->|"读写"| C2
B1G -->|"读写"| C2G
B5 -->|"读写"| C3
B5G -->|"读写"| C3
B6G -->|"读写"| C4G
style 员工端Web fill:#0d4f4a,stroke:#4ecdc4
style 顾客端Mobile fill:#4a3d0d,stroke:#f9a825
style 后端认证 fill:#3d1a4a,stroke:#a855f7
style 后端业务 fill:#4a1a1a,stroke:#ff6b6b
style 数据库表 fill:#4a4a1a,stroke:#ffd93d
八、两端认证方式对比
员工端和顾客端使用不同的认证策略和 Token 类型
graph TB
subgraph 员工端认证["🖥️ 员工端认证"]
E1["登录方式: 邮箱+密码"]
E2["Token类型: JWT (RS256)"]
E3["Access Token: 15分钟"]
E4["Refresh Token: 7天"]
E5["权限系统: RBAC"]
E6["中间件: auth-middleware.js"]
E1 --> E2 --> E3 --> E4 --> E5 --> E6
end
subgraph 顾客端认证["📱 顾客端认证"]
subgraph 注册会员["注册会员 (account_type='registered')"]
G1["登录方式: 邮箱/手机+密码"]
G2["Token类型: JWT (RS256)"]
G3["Access Token: 30分钟"]
G4["Refresh Token: 30天"]
G5["权限: 可访问会员体系"]
G1 --> G2 --> G3 --> G4 --> G5
end
subgraph 匿名顾客["匿名顾客 (account_type='anonymous')"]
A1["验证方式: 手机验证码"]
A2["Token类型: guest_token"]
A3["有效期: 7天"]
A4["权限: 仅预约功能"]
A1 --> A2 --> A3 --> A4
end
end
subgraph 功能权限["功能权限对比"]
F1["✅ 预约管理"]
F2["✅ 查看历史"]
F3["❌/✅ 积分系统"]
F4["❌/✅ 礼品卡"]
F5["❌/✅ 会员等级"]
end
注册会员 -->|"全部可用"| F1
注册会员 -->|"全部可用"| F2
注册会员 -->|"✅ 可用"| F3
注册会员 -->|"✅ 可用"| F4
注册会员 -->|"✅ 可用"| F5
匿名顾客 -->|"可用"| F1
匿名顾客 -->|"可用"| F2
匿名顾客 -->|"❌ 需注册"| F3
匿名顾客 -->|"❌ 需注册"| F4
匿名顾客 -->|"❌ 需注册"| F5
style 员工端认证 fill:#0d4f4a,stroke:#4ecdc4
style 顾客端认证 fill:#4a3d0d,stroke:#f9a825
style 注册会员 fill:#2d4a2d,stroke:#66bb6a
style 匿名顾客 fill:#4a4a2d,stroke:#ffd54f
style 功能权限 fill:#2d2d4a,stroke:#7986cb