Web 门店模块 — CUJ/EUJ 关键用户旅程

Store Management | 基础信息配置 + 员工导航 | 更新时间: 2026-02-10

范围声明

本模块定位: 仅用于门店基础信息配置(名称、地址、联系方式、营业时间)和门店上下文导航。
不在本模块处理: 门店服务管理、预约策略配置等复杂业务配置。

TOUCHES 追溯

📄 Pages: stores/page.tsx (门店列表/创建), stores/[id]/page.tsx (门店详情/编辑/员工跳转), employees/page.tsx (读取 store_id 上下文筛选)
🧩 Components: StoreTable.tsx, OperatingHoursDisplay.tsx, OperatingHoursEditor.tsx, StoreSelector.tsx
⚙️ Backend: api/stores.js ( GET /api/stores, POST /api/stores, GET /api/stores/:id, PUT /api/stores/:id )

架构概览

erDiagram
    stores ||--o{ employees : "has staff"

    stores {
        string id PK
        string name
        string code
        string address
        string city
        string state
        string zip_code
        string timezone
        jsonb business_hours
        jsonb contact_info
        boolean is_active
    }

权限矩阵

操作所需权限说明
查看门店列表/详情stores:view + 门店 scope需具备至少 1 家门店 scope(或 all stores)
创建门店stores:manage + superadmin 角色约束仅 superadmin 可创建,并受 checkEntityLimit('stores') 限制
编辑门店stores:manage + 门店 scope需具备目标门店范围且权限级别满足 manage
跳转员工管理employees:view + 门店 scope只做导航,员工增删改权限由员工模块独立校验

目录

CUJ 总览与优先级矩阵

CUJ优先级描述触发点业务价值E2E 状态
L1 P0 门店列表与创建 侧边栏 → Stores 门店基础资料入口 部分覆盖
L2 P0 门店详情、编辑与员工跳转 点击门店行 基础资料维护 + 跨模块导航 部分覆盖

CUJ-L1: 门店列表与创建

P0 基础信息配置入口 — 查看、搜索、创建门店

TOUCHES: stores/page.tsx, StoreTable.tsx, GET /api/stores, POST /api/stores

BDD 场景

场景 L1.1: 门店列表加载

Given 用户已登录,拥有 stores:view 权限,且具备门店 scope(至少 1 家门店,或 all stores)
When 导航到 /stores
Then 显示当前用户 scope 内的门店: 名称、编码、地址、状态
  And 支持按名称/编码搜索
  And 支持按状态筛选 (active / inactive)

场景 L1.2: 创建新门店

Given 用户已登录,角色为 superadmin(业务强制),并拥有 stores:manage 权限
When 点击“创建门店”并填写名称、地址、城市、州、邮编、电话、邮箱
Then 门店编码自动生成(generateStoreCode
  And 时区可由邮编自动推断(getTimezoneByZipCode
  And POST /api/stores 创建成功(受配额检查)

场景 L1.3: 创建门店配额限制

Given 用户已登录,角色为 superadmin,并拥有 stores:manage 权限
  And 当前租户 active 门店数量已达到套餐上限
When 尝试创建新门店
Then checkEntityLimit('stores') 拒绝请求并返回权限/配额错误

CUJ-L2: 门店详情、编辑与员工跳转

P0 维护门店基础资料,并从门店上下文进入员工管理

TOUCHES: stores/[id]/page.tsx, GET /api/stores/:id, PUT /api/stores/:id, /[locale]/[tenant]/employees?store_id={id}

BDD 场景

场景 L2.1: 查看门店详情

Given 用户已登录,拥有 stores:view 权限,且具备目标门店 scope
When 点击进入 /stores/{id}
Then 显示门店基础信息(名称、编码、地址、联系方式、时区)
  And 显示营业时间和基础统计(员工数、客户数)

场景 L2.2: 编辑门店基础信息

Given 用户已登录,拥有 stores:manage 权限,且具备目标门店 scope
When 在详情页点击“编辑”并修改名称、地址、联系方式、营业时间
Then 调用 PUT /api/stores/:id 更新成功
  And 页面刷新后显示最新门店信息

场景 L2.3: 跳转员工管理

Given 用户已登录,拥有 employees:view 权限,且具备目标门店 scope
When 在门店详情页点击“员工管理”
Then 跳转到 /employees?store_id={storeId}
  And 员工页面自动应用对应门店筛选(门店上下文保持)

EUJ-L: 异常与边界旅程

P1 负向路径覆盖 — 权限拒绝、scope 校验、配额限制

TOUCHES: GET /api/stores, POST /api/stores, PUT /api/stores/:id, GET /api/stores/:id, /employees?store_id={id}

BDD 场景

场景 EUJ-L1: 无门店查看权限访问列表

Given 用户已登录,但不具备 stores:view 权限(任意 scope)
When 访问 /stores 并触发 GET /api/stores
Then 返回 403 Forbidden
  And 页面不渲染门店列表数据

场景 EUJ-L2: 非 superadmin 尝试创建门店

Given 用户已登录且拥有 stores:manage 权限,但角色不是 superadmin
When 尝试创建新门店
Then 前端不展示创建入口
  And 若绕过前端直接调用 POST /api/stores,返回 403

场景 EUJ-L3: scope 不匹配导致门店编辑失败

Given 用户已登录并拥有 stores:manage 权限,但仅有门店 A 的 scope
When 发起 PUT /api/stores/{storeB}(B 不在 scope 内)
Then 返回 403 Forbidden
  And 门店 B 数据不发生变化

场景 EUJ-L4: 无员工查看权限时不展示员工跳转入口

Given 用户已登录并拥有 stores:view 权限,但不具备 employees:view
When 查看门店详情页
Then 不显示“员工管理”跳转按钮

跨模块链接

关联模块关联点说明
CUJ-D (Employees) 门店上下文员工导航 从门店详情跳转到员工页并携带 store_id
CUJ-C (Day-End) 门店详情页日结入口 门店详情页提供“开始日结”按钮
CUJ-N (Platform) 门店配额 checkEntityLimit 根据租户套餐限制门店数量

业务规则

#规则实现位置
1 创建门店仅 superadmin: 需 stores:manage 且角色为 superadmin POST /api/stores
2 门店编码自动生成: generateStoreCode(name, city),前端只读展示 stores/page.tsx
3 时区自动推断: 根据邮编通过 getTimezoneByZipCode() 推断 stores/page.tsx
4 员工管理入口仅导航: 入口显示受 employees:view + scope 控制 stores/[id]/page.tsx