Celoria 系统架构图

员工端 (Web) + 顾客端 (Mobile) 完整流程

员工端 Web (Next.js)
顾客端 Mobile Web
后端 Backend
数据库 Database
中间件 Middleware
一、三层架构总览
员工端 (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