版本:当前实现
面向系统:BiteGo 点点餐(Web 管理端)
项目定位:自助点餐系统的后台管理端
技术约束:Web 管理端 React + Material UI + TypeScript;服务端 Node.js + Express + TypeScript;前后端分离、REST 接口交互,使用 Yarn 作为包管理器。
1. 背景与问题
自助点餐系统需要“前台点餐能力 + 后台运营管理能力”的完整闭环。对门店而言,菜品分类、菜品与商品(SKU)的上架与库存、订单状态流转、桌台状态管理、门店基础信息维护,构成了日常运营的核心动作。
本项目的交付策略是:先搭建完整的 Web 管理端(前后端均具备),把数据模型与核心业务流程跑通,再开始开发面向顾客的前台点餐端(小程序)。
2. 产品目标
2.1 业务目标
- 建立“菜品/商品(SKU)—库存/价格—订单—桌台—门店信息”的后台管理闭环,支撑门店日常运营。
- 提供可控、可追溯的订单状态管理,减少误操作与重复操作带来的经营风险。
2.2 工程目标
- 管理端具备基础数据管理能力(菜品信息管理、订单信息查看等)。
- 模块划分清晰、可扩展;接口采用 REST 风格;数据库设计规范,满足一致性与完整性。
- 系统在常见误操作下具备基本容错能力(例如重复提交、空数据提交)。
3. 范围说明(当前实现)
3.1 功能范围
- 元数据管理:菜品分类管理
- 菜品与商品管理:
- 菜品基础信息管理(名称、描述、图片、销量)
- 规格管理(支持无规格/单规格/多规格;区分库存规格组与非库存规格组)
- 共享非库存规格组管理(可被多个菜品复用)
- SKU 管理(以 SKU 为单位管理价格、库存、上下架状态)
- 订单管理:
- 订单列表与详情(查看、筛选)
- 订单状态流转(创建、支付、制作、完成、取消、退款)
- 桌台管理:
- 桌台基础信息(桌号、桌台 ID)
- 桌台状态(空闲/占用)与会话管理(清台/强制清台/重置会话)
- 门店管理:
- 门店基础信息维护(名称、Logo、电话、地址、描述)
- 全店导出/全店恢复(用于初始化/迁移/恢复)
- 平台多租户(SaaS)管理能力:
- 平台级(SUPER_ADMIN):租户/门店/用户管理,平台 Branding 配置与平台概览
- 租户级(TENANT_ADMIN):租户门店管理,共享基础数据维护与同步状态查看
- 门店级(STORE_ADMIN):门店级数据管理;共享基础数据只读(UI 禁用 + 服务端强校验)
多租户总体设计与关键边界见 7.6-核心实现-平台多租户.md。
3.2 明确非范围
- 复杂促销(满减、券、会员积分)
- 完整财务对账、发票、进销存
- 用户评价与内容审核
- 配送与骑手系统
4. 术语与定义
- 菜品(Good):顾客看到的“菜”或“饮品”概念,如“奶茶”“童子鸡”。包含描述、图片、所属分类、销量等。
- 规格(Spec/Option):菜品的可选项,如“中杯/大杯”“加糖/不加糖”“辣度”等。规格按“规格组”组织,并分为“库存规格组/非库存规格组”。
- 共享非库存规格组(Shared Non-Stock SpecGroup):可被多个菜品复用的“非库存规格组”,在“规格管理”中统一维护;菜品仅绑定共享规格组,不重复配置规格项。
- 共享规格组描述:共享规格组可配置描述文本,仅用于菜品编辑页选择共享规格组时提示(不透出到小程序端)。
- 商品(SKU):仅由“库存规格组”的规格组合生成的可售卖单元,是库存、上下架状态的管理单元(SKU 数量不因非库存规格膨胀)。例如“奶茶 + 中杯”是一个 SKU。
- 价格项(PI / Price Item):订单/购物车中的最终计价单元,可关联 SKU + 非库存规格选择 + 营销优惠等;用于记录“非库存规格加价”等导致的最终单价。
- 桌台(Table):堂食场景下用于关联点餐过程的实体,具有编号与状态,可能关联“当前购物车/草稿单/未结单”。
5. 用户角色与权限模型(当前实现)
5.1 身份类型(登录主体)
- 后端管理员(ADMIN):可登录 Web 管理端;其“管理能力”由授权范围(AdminScope)确定的角色层级与按场景计算的
canManageSharedCatalog能力位共同决定。 - 前端用户(CUSTOMER):小程序用户,不参与 Web 管理端权限体系。
5.2 管理域授权角色(AdminScope.role)
- 平台超级管理员(SUPER_ADMIN):平台维度授权;具备全平台权限,并可在任意租户/门店上下文进行管理。
- 租户管理员(TENANT_ADMIN):租户维度授权;具备该租户管理能力,并自动具备该租户下所有门店管理能力。
- 门店管理员(STORE_ADMIN):门店维度授权;仅具备指定门店管理能力。
5.3 权限矩阵(按模块)
| 模块 | SUPER_ADMIN | TENANT_ADMIN | STORE_ADMIN |
|---|---|---|---|
| 平台概览(所有门店 block) | ✅ | ❌ | ❌ |
| 租户管理 | ✅ | ❌ | ❌ |
| 用户管理(全平台) | ✅ | ❌ | ❌ |
| 连锁门店管理(tenant 下 store) | ✅ | ✅(仅本 tenant) | ❌ |
| 共享基础数据(分类/菜品/规格) | ✅ | ✅(仅本 tenant) | ❌(只读) |
| 门店级数据(桌台/订单/SKU 库存与上下架) | ✅ | ✅(仅本 tenant 可选具体 store) | ✅(仅本 store) |
补充说明(实现口径):
- 统一以
/api/v1/admin/me/scopes返回的effectiveRole与canManageSharedCatalog做前后端权限校验;禁止使用/api/v1/users/me的role字段做任何“管理端权限判断”。 - 权限校验时按上下文(
X-Tenant-Id/X-Store-Id/X-Board)解析出当前作用域下的effectiveRole,再与接口所需最小角色 rank 做比较;共享菜单写类操作再额外检查canManageSharedCatalog。 - 支持混合授权组合:同一用户可同时拥有 “A 租户全量(含所有门店) + B 租户某门店” 等组合。
6. 全局交互与信息架构
6.1 左侧导航(建议)
- 概览
- 元数据管理
- 菜品分类
- 规格管理(共享非库存规格组)
- 菜品与商品
- 菜品管理
- SKU 管理(可合并在菜品详情内)
- 订单管理
- 订单列表
- 桌台管理
- 桌台列表
- 用户管理
- 门店管理
6.2 列表页通用规范
- 支持分页、关键字段搜索、筛选、排序(按创建时间默认倒序)。
- 表格列支持“复制 ID / 复制编号”等快速操作(如实现成本较高可延后)。
- 删除类操作默认“软删除”,并提供明确提示。
7. 功能需求(详细)
7.0 概览(后厨工作台 + 桌台总览)
概览页用于门店高频场景:快速查看全店桌台占用情况,以及后厨快速处理“待上菜商品”。页面采用左右分屏布局,默认实时刷新/实时推送(允许 1–3 秒延迟),并提供手动刷新兜底。
概览页增强(消息与播报):
- 顶部右侧增加通知中心入口(铃铛),与用户头像昵称并列。
- 管理员可在概览页开启/关闭“播报”开关:当收到新下单/退款申请事件时,语音播报并在页面展示字幕;并发事件使用队列顺序播报。
7.0.1 布局与入口
- 页面入口:左侧导航「概览」
- 页面布局:左右两列(约 1:2)
- 左屏:桌台总览(全桌台状态)
- 右屏上:活跃订单(未上菜商品列表,供后厨使用)
- 右屏下:所有订单(仅在选中某个桌台后展示;展示该桌台当前会话的全部订单与菜品明细,并汇总总数量/总金额,排除已退款订单)
7.0.2 左屏:桌台总览
展示目标
- 一屏看清全店桌台“空闲/占用”状态
- 通过 hover 快速了解该桌台的开台时间、当前购物车、关联订单
桌台卡片(列表/宫格)
每个桌台至少展示:
- 桌号(Table.code)
- 状态:空闲 / 占用(强视觉区分)
- 在线人数(实时):该桌台当前活跃会话连接数(用于门店判断是否有人在点餐)
- 快速摘要(建议):
- 开台时间(openedAt)
- 购物车数量/金额(cartItemCount/cartTotalAmount)
- 未完成订单数(activeOrderCount)
- 该桌台总订单数(totalOrderCount)
- 该桌台总金额(totalAmountExRefunded,不含已退款订单)
hover(或点击浮层)展示:
- 开台时间 openedAt
- 当前购物车摘要:
- SKU/规格文本、数量、小计(支持滚动)
- 添加人提示(若小程序端支持展示“谁加了什么”,则 Web 端也必须可见):添加人昵称/头像(快照或实时用户信息)
- 更新时间 updatedAt
- 关联订单列表(最近 N 条,默认 N=3,可配置):
- 订单号(短号)、状态、创建时间、金额
- 点击可跳转到订单详情
开台时间定义
openedAt 作为“桌台会话”的开始时间,取以下事件中最早发生的时间:
- 该桌台购物车首次从空变为非空的时间;若无购物车,则取该桌台第一笔订单创建时间
可操作项
- 点击桌台卡片:跳转至“桌台列表”并自动定位/筛选该桌台(或进入桌台详情页,若实现)
- 桌台总览表格模式(若采用表格):点击桌号可快捷复制
tableId到剪贴板,并通过全局提示反馈结果。 - 桌台总览表格模式(若采用表格):点击整行可切换“选中桌台”状态;右侧“活跃订单”仅展示该桌台下的活跃订单;再次点击可取消选中并恢复全量展示。
- 可选快捷操作(仅在 hover 浮层中提供):
- 刷新该桌台数据
- 重置桌台购物车(遵循 7.6.3 风险控制规则)
- 关台(清台):仅当该桌台无未完成订单时可操作
- 强制清台:存在未完成订单时的兜底操作,需二次确认并提示风险
交互补充(易用性):
- 桌台总览数据量较大时,表格表头需吸附(sticky)在顶部,避免滚动后看不到列含义。
- 为保证吸顶效果稳定,桌台总览建议在固定高度的内容区内滚动(而非整页滚动)。
7.x 账号与用户管理
顶部用户入口
- 顶部右侧“退出登录”替换为「用户头像 + 用户昵称」,点击弹出菜单:
- 个人信息:进入个人信息页
- 退出登录:清除 token 并回到登录页
7.x.1 共享数据同步状态(租户主门店)
说明:当前“共享数据同步”采用手动批量触发(非每次变更自动触发)。租户主门店需要清晰感知“是否存在未同步变更”,并可一键触发同步任务。
- 展示位置:Web 管理端右上角 App Bar(与通知铃铛、用户头像同一行),仅在以下条件展示:
- 当前登录人角色为
TENANT_ADMIN(或SUPER_ADMIN以 tenant 视角进入) - 当前上下文为“连锁主门店(Primary Store)”
- 当前登录人角色为
- 状态类型(视觉可用颜色/图标区分):
- 已同步:无未同步变更
- 待同步:存在未同步变更(展示红点/Badge)
- 同步中:存在进行中的同步任务(展示 loading/转圈)
- 同步失败:存在失败任务(展示错误态)
- hover(或点击浮层)展示详情:
- 未同步变更摘要:分类/菜品/规格组等的新增/更新数量(按模块统计)
- 变更详情(最多展示最近 N 条,默认 N=10):对象类型、名称、变更时间、变更人(若可得)
- 同步任务摘要:目标门店数量、PENDING/RUNNING/SUCCEEDED/FAILED 数量、上次同步时间
- 操作按钮:
- 「立即同步」:触发一次批量同步任务(并展示“已提交任务/同步中”反馈)
- 「查看详情」:进入“同步详情页/弹窗”,可查看更完整的变更清单与任务执行结果(可后续扩展)
- 刷新策略(与当前实现一致):
- WebSocket 连接可用时:订阅同步状态快照,由服务端推送更新
- WebSocket 不可用时:按 30 秒间隔轮询接口刷新
个人信息页(管理员)
- 支持修改头像、昵称
- 支持修改密码(旧密码校验 + 设置新密码)
- 保存后应立即生效(顶部头像昵称同步更新)
用户管理页
- 支持筛选查看:
- 小程序普通用户(CUSTOMER)
- Web 管理端管理员(ADMIN)
- 三个视角(platform / tenant / store)统一使用同一套 UI 交互:
- 顶部仅有单一入口「添加成员」,点击弹出
AddMemberDialog,内含双 Tab:- 「新建账号」:录入用户名/密码/昵称(用户名提交前实时校验可用性),成功后直接在当前作用域授予默认角色(platform 视角可一次性勾选多条租户/门店 scope;tenant 视角默认
TENANT_ADMIN;store 视角默认STORE_ADMIN)。 - 「从已有账号选择」:按用户名/昵称搜索已有账号并为其在当前作用域新增 scope,避免重复创建账号导致账号膨胀。
- 「新建账号」:录入用户名/密码/昵称(用户名提交前实时校验可用性),成功后直接在当前作用域授予默认角色(platform 视角可一次性勾选多条租户/门店 scope;tenant 视角默认
- 列表行点击进入右侧
UserDetailDrawer:展示基础资料与该账号在可见范围内的全部 scope(平台视角列出全部;租户视角仅列当前租户下的;门店视角仅列当前门店的)。 - Drawer 内支持两类维护操作:编辑对方昵称/头像(走
PUT /api/v1/admin/users/:userId/profile)与重置密码(走POST /api/v1/admin/users/:userId/reset-password)。两者均在服务端按"目标用户需在调用者作用域内至少有一条 ACTIVE scope"做授权校验,避免跨租户/跨门店越权操作他人账号。 - Drawer 内的"作用域列表"采用只读 Chip + Tooltip 展示(租户/门店名称、角色、创建时间),不提供单行删除按钮,以防止高频场景下的误删;作用域的授予/撤销只能从 platform 视角的专属权限页进行。
- 顶部仅有单一入口「添加成员」,点击弹出
- 角色选择器约束:
STORE_ADMIN仅适用于CHAIN租户的分店;当平台视角为一个SINGLE租户新增 scope 时,STORE_ADMIN选项自动置灰并以 hover Tooltip 说明"单店租户由租户管理员统一管理"。- 租户/门店视角的角色默认值分别固定为
TENANT_ADMIN/STORE_ADMIN,不提供降级选项。
租户/门店创建流程约束:
- 新建连锁与新建单店时不创建管理员账号;账号与权限通过"用户管理"显式创建/绑定。
7.y 通知中心(可扩展)
入口与展示
- 入口:页面顶部右侧铃铛按钮(在用户头像旁)
- 点击弹出通知列表(支持滚动,展示最近 N 条)
- 通知包含:下单、退款申请等事件(后续可扩展更多类型)
交互
- 通知展示基础字段:标题、摘要、时间、关联订单(若有)
- 支持标记已读
- 支持“一键已读”:将当前所有未读通知批量标记为已读
- 对需要处理的通知(如退款申请)提供“去处理”入口(跳转订单详情/订单列表进行审核处理)
7.0.3 右屏:活跃订单(未上菜商品,后厨用)
展示目标
- 面向后厨,将“需要制作/上菜的商品”集中展示,并能快速标记“已上菜”
- 减少在订单列表中逐单点开的成本
活跃范围定义
活跃订单指需要后厨处理的订单,满足任一条件:
- 订单状态为 Paid 或 Making
- 订单存在未上菜商品(OrderItem.servedQty < OrderItem.qty)
右屏默认仅展示“未上菜商品”,并按订单聚合展示;可提供“按商品聚合”切换作为扩展能力。
列表结构(按订单聚合)
每个订单块至少展示:
- 桌号、订单号、下单时间/支付时间(可选)
- 订单状态(Paid/Making)
- 未上菜商品列表:
- 菜品名称(快照)、规格文本(快照)
- 待上菜数量(pendingQty = qty - servedQty)
- 快捷按钮:上菜(+1)、全部上齐(将该条明细 servedQty 置为 qty)
- 订单级快捷操作(可选):
- 一键上齐本单(将本订单所有明细 servedQty 置为 qty)
- 进入订单详情
上菜与订单状态自动流转规则
- 上菜动作本质为更新订单明细的 servedQty(或 served 状态),需要幂等且可并发。
- 自动状态流转:
- 当某订单首次发生上菜(任一明细 servedQty 从 0 变为 >0)且订单状态为 Paid,则订单状态自动流转为 Making。
- 当某订单所有明细均已上齐(对所有 OrderItem:servedQty == qty),订单状态自动流转为 Completed。
- 退款与取消约束:
- 对状态为 Canceled/Refunded/Completed 的订单,禁止上菜操作,并在右屏不展示或展示为不可操作的历史状态。
权限与审计
- 右屏上菜操作允许具备门店级(
STORE_ADMIN及以上)作用域的管理员执行。 - 上菜操作需记录:操作者、时间、订单号、明细项、变更前后 servedQty。
7.1 元数据管理 —— 菜品分类
7.1.1 分类字段
- 分类 ID(系统生成,不可编辑)
- 分类名称(必填,1–20 字,门店内唯一)
- 分类副标题(可选,0–50 字)
- 分类小标签(可选,0–10 字):用于点餐页分类电梯角标展示
- 分类排序(整数,默认 0;数字越大越靠前)
- 说明:当前实现的列表默认只展示“启用(ACTIVE)”分类;停用通过“删除(软停用)”实现(见下方)。
7.1.2 功能点
- 新增分类:校验名称唯一;创建成功后可继续新增。
- 编辑分类:允许修改名称、副标题、小标签与排序。
- 删除分类(软停用):从“启用分类列表”移除,用于前台隐藏该分类。
- 排序:支持编辑排序值;支持拖拽排序(拖拽后会自动重算所有分类排序值)。
7.1.3 验收标准
- 能创建/编辑/停用分类;分类名称唯一性校验生效。
- 分类停用后,菜品列表筛选与前台可见性规则一致(由后端统一控制)。
7.2 菜品管理(Good)
7.2.1 菜品字段(基础信息)
- 菜品 ID(系统生成)
- 菜品名称(必填,1–40 字)
- 菜品描述(可选,0–200 字):一句话描述,用于点餐页列表等紧凑区域展示
- 菜品详情(可选,Markdown 富文本):用于点餐页菜品七分屏展示更详细的介绍(支持标题/列表/图片等)
- 菜品图片(可选,多张,仅上传添加)
- 允许上传/维护多张图片(用于详情轮播/展示);列表封面默认取第 1 张图
- 图片编辑交互参考微信朋友圈:正方形缩略图网格、支持拖拽排序、右上角可删除、末尾为虚线加号格(点击唤起上传)
- 支持“设为封面图”(将该图移动到第 1 位)
- 图片上传通过后端文件上传接口获取 URL,并保存到菜品信息中
- 支持 JPG/PNG/GIF 等格式,单文件建议 ≤ 10MB
- 所属分类(必选,可多选)
- 支持同一菜品绑定多个分类,用于“本店甄选/活动推荐”等高优分类复用并高亮同一菜品
- 销量(数值,默认 0)
- 推荐规则:销量为“有效订单明细累计销量”的派生值
- 下单成功(Paid)时累加对应菜品的下单数量
- 若订单后续被取消/退款,则回滚扣减对应数量(避免销量长期偏大)
- 如需支持手工修正,可提供“初始化销量”字段或允许管理员编辑。
- 推荐规则:销量为“有效订单明细累计销量”的派生值
- 菜品状态(建议):启用/停用(与 SKU 上下架区分)
- 创建/更新时间(只读)
7.2.2 菜品列表页
- 搜索:按菜品名称模糊搜索
- 筛选:分类、菜品状态、是否有规格(可选)
- 筛选记忆:状态、分类、名称、当前页与每页条数按当前页面(平台共享、租户共享、门店三套入口分别记忆)写入
sessionStorage,进入菜品详情后返回列表,筛选条件保持不变;浏览器关闭/Tab 关闭后自动失效 - 列表展示:图片缩略图(可点开预览)、名称、分类、状态(中文展示)、起步价/基础价、销量、操作
- 分类内排序:当筛选了具体分类时,列表进入“分类内排序模式”,提供 DragIndicator 列与排序值列,支持拖拽调整该分类下菜品展示顺序(仅在当前分类内生效)
- 操作:
- 新增菜品
- 编辑菜品
- 进入“规格与 SKU 管理”
- 停用/启用菜品(停用后所有 SKU 自动下架或前台不可售,需定义一致规则)
- 批量操作(推荐):批量上架/下架、批量调整分类
- SKU 概览(推荐):在菜品列表支持展开行展示 SKU 列表(只读:规格组合/价格/库存/状态),SKU 的编辑仍在菜品详情页完成
7.2.3 菜品详情页(规格与 SKU 管理入口)
菜品详情至少包含:
- 基础信息编辑区
- 规格管理区(见 7.3)
- SKU 列表区(见 7.4)
7.3 规格管理(Spec)
7.3.1 规格模型
一个菜品可配置 0~N 个选项组(OptionGroup),每个选项组包含若干选项(Option)。选项可配置“加价”,并参与 SKU 自动生成与最终价格计算。除“自定义规格组”外,支持绑定“共享非库存规格组”,其规格项在“规格管理”中维护。
- 选项组(OptionGroup)
- 选项组名称(必填,1–10 字,如“杯型”“甜度”“辣度”)
- 是否必选
isRequired - 最小选择数
minSelection/ 最大选择数maxSelection - 选项列表
- 共享非库存规格组(Shared SpecGroup)
- 仅支持“非库存”类型,不参与 SKU 组合生成
- 规格项/默认选项/选择数规则在“规格管理”中统一配置,菜品仅做绑定
- 选项(Option)
- 选项名称(必填,1–10 字,如“大杯”“不加糖”)
- 加价
price(以分为单位,>= 0) - 状态:启用/停用(停用会影响 SKU 生成)
多选规格组规则:
- 可选组/必选组使用
isRequired区分,允许maxSelection > 1。 - 选择规则:
isRequired=true:最少选择数minSelection >= 1isRequired=false:最少选择数minSelection = 0minSelection <= maxSelection <= options.length
- 使用场景示例:
- “加料/配料”多选:可选组,
min=0,max=2(最多加两种) - “双拼口味”必选多选:必选组,
min=2,max=2(必须选两种口味) - “辣度/甜度/杯型”单选:必选组,
min=1,max=1
- “加料/配料”多选:可选组,
7.3.2 规格配置规则
- 无规格菜品:不配置任何规格组,此时系统自动生成 1 个 SKU(默认 SKU)。
- 有规格菜品:选项组数量 ≥ 1;SKU 仅由“库存规格组”的启用选项做笛卡尔积生成。
- 多选规格组:先在单个选项组内生成“组选组合”(组合大小 k ∈ [minSelection,maxSelection]),再对各组选组合做笛卡尔积生成 SKU。
- SKU 唯一性:同一 Good 下,同一组合(specKey)仅允许一个 SKU。
- 价格计算规则:
- Good 维护
basePriceCents(基础价格,单位:分)
- Good 维护
- SKU 价格 = Good.basePrice + “库存规格组”选项加价之和(单位:分)
- PI 单价 = SKU 价格 + “非库存规格组”选项加价之和(单位:分)
- 规格变更的 SKU 影响规则:
- 新增选项:允许一键“重新生成 SKU”,并提示将新增若干 SKU(初始库存为 0、状态为下架)。
- 删除/停用规格值:不允许硬删除;停用后相关 SKU 自动下架且不可售卖;历史订单仍可展示该规格文本(订单快照字段)。
- 修改规格名称:不影响历史订单展示(历史订单展示使用下单时的快照)。
界面操作指引(Web 管理端):
- 在“规格组”中配置:
- 规格组名称
- 规格组类型:库存规格组 / 非库存规格组 / 共享非库存规格组
- 是否必选(必选/可选)
- 最少选择数、最多选择数
- 选项名称与加价
- 规格组排序:创建/编辑时支持调整顺序(影响前台展示顺序)
- 当选择“共享非库存规格组”时:
- “规格组名称”改为下拉选择共享规格组
- 允许额外配置“禁用规格项”“默认规格覆盖”(不设置则使用共享规格组自身默认规格;若覆盖项被删除则使用共享规格组自身默认规格)
- 规格组自身配置项(必选/最少/最多/默认选项/选项加价)在菜品页只读并隐藏,由共享规格组自身配置决定
- 共享规格组可在“规格管理”页面统一维护,并显示关联菜品数量
- 保存后由服务端按“库存规格组”自动重新生成 SKU;SKU 列表支持批量上架/下架与批量设置库存。
- 若对“已有菜品”进行“库存规格组”的结构变更(影响 SKU 组合集合,如增删库存规格组/规格值、调整必选与选择数量约束、切换规格组类型等),属于破坏性操作:将删除该菜品所有历史 SKU,并清空库存/上下架等 SKU 级设置;界面必须弹出确认对话框后才允许提交。仅修改加价/名称/排序不会触发破坏性确认与重建。
7.4 SKU 管理(库存 / 价格 / 状态)
7.4.1 SKU 字段
- SKU ID(系统生成)
- 关联菜品 ID
- SKU 规格组合展示(如“中杯 / 不加糖”)
- 售价(必填,>= 0)
- 服务端存储与计算统一使用“分(cents)”的整数
- Web 管理端录入可按“元(两位小数)”展示与输入,提交时由接口层完成转换
- 库存(整数,>= 0)
- 状态:上架 / 下架 / 删除(删除为软删除)
- 条码/编码(可选)
- 创建/更新时间
交互补充:
- SKU 价格在管理端只读展示(由基础价 + 选项加价规则计算),不提供手工编辑入口
- SKU 支持批量操作:批量上架/下架、批量设置库存
- 默认规格:每个规格组支持配置默认选项(也可不配置)。管理端在“规格组编辑”中设置:
- 单选规格组:最多选择 1 个默认选项
- 多选规格组:最多选择
maxSelection个默认选项 - 前台默认选中逻辑以规格组默认选项为准
7.4.2 SKU 列表(在菜品详情页内或独立页面)
- 分页:每页 10 条
- 搜索筛选:支持按“规格组合/skuId”关键字筛选
- 支持批量操作:
- 批量上架/下架(仅对未删除 SKU 生效)
- 批量设置价格(可选)
- 批量调整库存(建议支持“增减库存”与“设置库存”两种)
- 上架校验:
- 价格必须已设置
- 库存 > 0(当前实现默认要求;库存为 0 的 SKU 不可售)
- 删除规则:
- 若 SKU 存在历史订单引用,仍允许软删除,但需要提示“仅影响未来售卖,不影响历史订单展示”。
7.4.3 验收标准
- 支持无规格菜品自动生成默认 SKU。
- 支持多规格组笛卡尔积生成 SKU,并能按 SKU 维度管理库存/价格/上下架。
- 上架规则校验生效;删除为软删除且不会破坏历史订单展示。
7.5 订单管理
7.5.1 订单状态定义
订单状态包含:
- 创建(Created):订单已生成但未支付(或未确认支付)。
- 支付(Paid):订单支付成功。
- 制作(Making):门店开始制作/出餐。
- 完成(Completed):订单已完成(顾客取餐/核销)。
- 取消(Canceled):订单被取消(可能发生在未支付或支付后特殊场景)。
- 退款中(Refunding):订单已发起退款申请,等待门店审核/退款处理完成。
- 退款(Refunded):订单已退款完成(可由取消后进入或支付后直接进入)。
7.5.2 状态流转规则(建议)
- Created → Paid → Making → Completed(主链路)
- Created → Canceled(未支付取消)
- Paid → Refunding → Refunded(支付后退款:申请退款 → 审核通过并退款完成)
- Refunding → Paid(审核不通过)
- Paid → Canceled(若业务允许“支付后取消”,则 Canceled 与 Refunded 需区分;当前实现中支付后取消通过退款流程表达)
- Any →(不可逆)Completed/Refunded(终态)
要求:
- 每次状态变更必须记录:操作者、时间、原因(可选)、备注(可选)。
- 状态变更需要二次确认弹窗,避免误操作。
- 状态变更需具备幂等:重复点击不会产生多次变更或异常数据。
模拟支付/退款口径(需对齐小程序端)
- 模拟支付:用户在小程序端“提交订单”后,服务端创建订单并立即模拟支付成功,使订单状态直接进入
Paid(Created仅作为服务端瞬时中间态,前端默认不展示“待支付”)。 - 模拟退款(含审核):用户在小程序端发起“申请退款”后,订单状态进入
Refunding,服务端创建退款流水进入REVIEWING;Web 管理端审核通过后退款流水进入PENDING,10 秒后自动完成退款并将订单状态流转为Refunded;审核不通过则订单状态变更为Paid,并记录驳回原因用于追溯。 - 流水表要求:即使为模拟支付/退款,也必须记录完整支付流水与退款流水(用于审计、排障与后续接入真实支付的平滑演进)。
7.5.3 订单列表页
- 筛选:
- 状态(多选)
- 下单时间区间
- 桌台(可选)
- 订单号(精确/模糊)
- 列表列:
- 订单号、桌台、总金额、商品数量、状态、创建时间/支付时间(可选)、操作
- 操作:
- 查看详情
- 修改状态(按状态显示可用动作按钮)
- 导出(CSV/Excel)
7.5.4 订单详情页
- 订单基础信息:
- 订单号、状态、下单时间、支付时间、桌台信息、金额(应付/实付/退款)、备注
- 商品明细:
- 菜品名称(快照)、规格快照、单价、数量、小计
- 状态流转记录:
- 操作人、时间、从何状态到何状态、备注
- 上菜明细(建议):
- 展示每个 OrderItem 的已上菜数量 servedQty 与待上菜数量
- 展示上菜操作记录(若实现审计日志)
- 操作:
- 打印:调用浏览器打印,打印模板包含门店信息、订单号、时间、桌台、明细、合计
- 导出:导出当前订单明细(CSV)
7.5.5 导出字段规范
订单导出(列表级)字段至少包含:
- 订单号、状态、桌台编号、下单时间、支付时间、总金额、商品总数
订单明细导出字段至少包含:
- 订单号、菜品名称、规格、SKU 标识(可选)、单价、数量、小计
7.5.6 与小程序端联动(订单同步)
Web 管理端作为门店侧的“订单处理端”,其订单状态变更需要与小程序端保持一致并可及时感知:
- 状态一致性:订单状态机定义与流转规则必须与小程序端一致(Created/Paid/Making/Completed/Canceled/Refunded)。
- 状态一致性:订单状态机定义与流转规则必须与小程序端一致(Created/Paid/Making/Completed/Canceled/Refunding/Refunded)。
- 实时可见性:后台对订单进行状态变更(例如 Paid→Making→Completed、Paid→Refunded)后,小程序端的“桌台订单页”和“历史订单详情页”需同步更新,允许 1–3 秒延迟。
- 幂等与防误操作:
- 订单状态变更接口必须幂等(重复点击不产生多次变更)。
- 状态变更需二次确认;对非法流转给出明确提示并拒绝。
7.6 桌台管理
7.6.1 桌台字段
- 桌台 ID(系统生成)
- 桌台编号(必填,1–10 字/数字,如“A01”“1号桌”,门店内唯一)
- 桌台状态:空闲 / 占用
- 桌台二维码链接(系统生成,HTTPS 静态链接,用于小程序扫码入桌)
- 关联信息(只读,按实现选择):
- 当前购物车/草稿单 ID(可选)
- 当前未结订单号(可选)
- 更新时间
7.6.2 桌台列表页
- 搜索:桌台编号
- 筛选:状态
- 操作:
- 新增桌台
- 查看/复制二维码链接(用于发给门店打印或生成桌贴)
- 下载/打印桌台二维码(可选:打印模板包含门店名 + 桌号 + 二维码)
- 编辑桌台编号
- 切换状态(空闲 ↔ 占用)
- 重置桌台购物车
- 清台
- 强制清台
交互补充:
- 新增/编辑桌台时默认创建为空闲态(FREE),不要求在表单中选择状态
- 空闲态桌台不展示“强制清台”按钮(避免误操作)
7.6.3 桌台状态与购物车规则
- 桌台“占用”的判定:
- 桌台存在未结订单(Created/Paid/Making)或存在购物车草稿数据,则为占用。
- 若通过手动切换状态,也必须同步处理“是否允许在空闲状态保留购物车”等规则(当前实现为空闲状态不保留会话购物车)。
- 重置桌台购物车:
- 清空该桌台的购物车/草稿数据(不影响已生成订单)。
- 若桌台存在未结订单,重置操作需二次确认并提示风险。
清台
为与小程序端“桌台会话/浮窗”一致,建议明确清台语义:
- 清台 = 将桌台状态置为“空闲” + 清空桌台购物车 + 结束当前桌台会话
- 清台前置条件:
- 桌台不存在未结订单(Created/Paid/Making),否则禁止清台并提示“存在未完成订单”
- 若仅存在已完成/已取消/已退款订单,允许清台
- 清台后联动:
- 小程序端桌台会话失效,桌台浮窗自动隐藏;再次点餐需重新扫码开台
强制清台(必做)
允许管理员在“存在未结订单”或“用户端异常无法退出”等情况下强制结束桌台会话,用于快速止损与恢复门店运营。
- 行为定义:
- 强制清台 = 将桌台状态置为“空闲” + 清空桌台购物车 + 强制结束当前桌台会话(会话版本号 +1)
- 不自动取消/退款已有订单:订单仍保留原
tableId与会话版本信息,后厨/门店可继续处理;新扫码进入桌台会话的用户不会看到上一会话的订单列表
- 风险提示:
- 操作必须二次确认,并提示“将强制踢出该桌台所有在线用户端”
- 需记录操作日志(操作者、时间、桌台、原因)
- 端侧联动:
- 小程序端收到“桌台已关闭”事件或接口返回后,展示“桌台已关闭”提示页,引导用户返回主页重新扫码入桌
- Web 概览页与后厨工作台应实时更新该桌台状态与关联视图(允许 1–3 秒延迟)
7.6.4 验收标准
- 桌台编号唯一性校验生效。
- 能完成桌台状态轮转;支持重置购物车并符合风险控制规则。
7.6.5 与小程序端联动(桌台会话与共享购物车)
小程序端以“桌台”为协同上下文支持多人同时点餐,因此 Web 管理端需要具备对桌台会话数据的可视化与实时感知能力,用于门店/后厨协同:
- 桌台购物车实时展示:
- Web 管理端需能查看每个桌台当前购物车(SKU 明细、规格文本、数量、金额、更新时间)。
- 购物车明细需包含“添加人”信息:用于支持小程序端展示“订单/购物车内商品由谁添加”,并便于门店排查协同操作。
- 桌台购物车为“桌台共享草稿”,多人加购/删改数量会实时同步到该桌台的所有端(小程序多端 + Web 端)。
- 允许 1–3 秒延迟;异常时需提供“手动刷新”能力。
- 桌台订单聚合展示:
- Web 管理端需能查看桌台关联订单列表(尤其是未结算/未完成:Created/Paid/Making)。
- 支持从桌台维度快速进入订单详情与处理(制作/完成/退款)。
- 并发与库存校验口径:
- 购物车编辑与下单提交时均以服务端校验为准;当库存不足或 SKU 下架导致操作失败时,Web 端与小程序端需能看到一致的失败原因与最新数据。
7.7 门店管理
7.7.1 字段
- 门店名称(必填,1–40 字)
- 连锁门店对外展示名由服务端统一拼装为「品牌名(门店名)」,管理端在输入框下方显示实时预览提示以降低命名歧义。
- 门店照片(可选:URL 或上传)
- 图片上传通过后端文件上传接口获取 URL,并保存到门店信息中
- 连锁门店(
tenantType='CHAIN')的门店 Logo 不可单独编辑,由租户“品牌 Logo”统一同步:品牌 Logo 修改时级联覆盖全部门店logoUrl;新建子门店时直接采用当前品牌 Logo。管理端在此场景禁用 Logo 输入与上传按钮并给出指引。
- 联系电话(必填,格式校验)
- 门店地址(必填,0–100 字)
- 门店描述(可选,0–500 字)
- 营业时间(可选)
7.7.2 功能点
- 查看门店信息
- 编辑与保存(仅管理员)
- 连锁租户门店保存时服务端会校验 Logo 字段,若与当前值不一致将拒绝写入。
- 门店信息在“打印模板/导出”中复用(如订单小票抬头)
- 门店展示名由服务端计算并写入
displayName,所有使用方(管理端表格、概览、扫码落地页、小程序门店头部、历史订单等)直接消费该字段,避免端侧拼装不一致。
7.7.3 数据搬家(平台 / 连锁 / 门店)
背景:测试环境易出现测试数据污染;生产环节也可能需要“初始化/搬家/恢复”能力。当前实现将数据搬家统一为三层入口:
- 平台搬家(平台设置内):
- 导出平台快照 / 清空平台 / 恢复平台(完全还原,保留 ID)
- 恢复/清空期间平台进入维护态并强制 token 失效(需重新登录)
- 连锁搬家(品牌管理内,tenant 视角):
- 导出租户快照 / 清空租户 / 恢复租户(克隆式:重建配置类 ID、重建关联、重建桌台二维码)
- 恢复/清空期间租户进入维护态:禁写 + 断连 + 阻止新连接;AppBar 展示「恢复中」(不强制 token 失效)
- 从独立门店恢复到连锁主门店(用于“独立店升级为连锁”):
- 管理端上传“独立门店导出文件(全店导出)”
- 服务端自动执行:清空并删除当前租户下所有子门店与旧主门店 → 新建一个干净的主门店 → 将独立门店数据导入到新主门店
- 风险提示:该操作会改变主门店
storeId;恢复完成后需重新选择主门店上下文
- 门店搬家(门店管理内,store 视角):
- 入口展示约束:仅单店租户(
tenantType='SINGLE',即“独立门店”)展示该入口 - 导出门店配置 / 清空门店 / 恢复门店(克隆式:重建配置类 ID、重建关联、重建桌台二维码)
- 恢复/清空会清理该门店业务数据与配置数据,不影响平台顾客用户与管理员账号
- 恢复/清空期间门店进入维护态:禁写 + 断连 + 阻止新连接;AppBar 展示「恢复中」(不强制 token 失效)
- 入口展示约束:仅单店租户(
8. 数据模型(概念级)
8.1 核心实体
- Store:门店
- Tenant:租户/连锁品牌
- UserRole:用户-租户/门店角色映射
- PlatformConfig:平台信息(平台名称/Logo)
- Category:分类
- Good:菜品
- SpecGroup:规格组(归属 Good)
- SpecOption:规格值(归属 SpecGroup)
- SKU:商品(归属 Good,包含规格组合)
- Table:桌台
- TableCart:桌台购物车(桌台共享草稿)
- TableCartItem:桌台购物车明细(按 SKU 聚合)
- Order:订单(可关联 Table)
- OrderItem:订单明细(快照 GoodName / SpecText / UnitPrice)
- OrderItem.servedQty:已上菜数量(用于后厨工作台与自动流转)
8.2 关键约束
- 单门店场景:Category.name 门店内唯一;Table.code(桌台编号)门店内唯一
- 多租户场景:
- 所有业务数据必须归属到
tenantId/storeId(共享数据按 tenant 归属,门店数据按 store 归属) - Category.name 在 tenant 范围内唯一(共享分类);门店可读但不可写
- Table.code 在 store 范围内唯一(门店级)
- 所有业务数据必须归属到
- SKU 唯一性:同一 Good 下,同一规格组合仅允许一个 SKU
- TableCart 版本控制:桌台购物车建议带 version 字段,支持并发编辑与幂等操作
- TableCartItem 归并规则:同一桌台下,若需要展示“添加人”,则购物车明细的聚合键至少包含 skuId + addedByUserId(避免不同人加的同款被合并后丢失归属)
- 订单明细必须保存快照字段,避免后续改名影响历史订单展示
- OrderItem.servedQty 约束:0 <= servedQty <= qty,且并发更新不可超量
9. 非功能性需求
9.1 性能与体验
- 管理端列表页在“千级数据量”下分页查询可用,提供分页选择器(页码切换 + 每页条数选择),默认每页 50(可选 10/20/50/100)。
- 核心操作(上架/下架/状态变更)给出明确成功/失败反馈,并避免重复提交。
9.2 安全与审计
- 登录态采用 Token(如 JWT 或 Session),接口必须鉴权。
- 关键操作(订单状态变更、库存变更、删除)建议记录操作日志(操作者、时间、对象、前后值)。
- 输入校验:价格/库存/手机号/必填字段校验在前后端同时存在,以后端为准。
9.3 可靠性与容错
- 订单状态变更接口必须幂等。
- 桌台购物车编辑接口必须幂等,避免重复点击导致多次叠加。
- 对常见误操作(空字段提交、重复点击、非法状态流转)给出可理解的错误提示。
- 桌台购物车与订单状态变更需支持实时订阅/推送,允许 1–3 秒延迟,并提供回源刷新能力。
- 上菜操作需支持并发与幂等:同一明细的 servedQty 更新必须避免超出 qty,并对并发更新给出确定性结果。
10. 验收清单
概览:
- 左右分屏可用;左屏可展示全桌台空闲/占用状态并可 hover 查看开台时间、购物车、关联订单。
- 右屏可展示所有活跃订单的未上菜商品,并支持快捷上菜;一单商品全部上齐后订单状态自动流转。
分类:新增/编辑/停用可用,名称唯一性校验正确。
菜品:新增/编辑/查询可用;图片字段可录入;分类绑定正确。
规格与 SKU:
- 无规格菜品自动生成默认 SKU;
- 多规格组生成 SKU,SKU 可设置价格/库存/上架下架;
- 上架校验生效(价格/库存)。
订单:
- 列表筛选、查看详情可用;
- 状态流转符合规则且有二次确认;
- 支持导出与打印(至少 CSV + 浏览器打印)。
桌台:
- 新增/编辑/状态切换可用;
- 支持重置购物车并有风险控制策略。
门店:
- 门店基础信息可维护;打印模板中可展示门店信息。
多租户与权限:
- SUPER_ADMIN:可查看平台概览;可管理租户/门店/用户;可配置平台名称/Logo(供小程序首页首次展示)
- TENANT_ADMIN:可管理租户下门店;可维护共享基础数据;子门店管理员只读
- STORE_ADMIN:可管理门店级数据(桌台/订单/库存/上下架),共享数据不可写
12. 附录A:统一数据模型标准(Web/小程序共用)
本附录用于对齐 Web 管理端与小程序端在数据模型层的字段口径、约束与关系结构,作为两端 PRD 的共同标准。若后续迭代新增字段,两端 PRD 需同时更新本附录并保持一致。
12.1 标识与字段类型约定
- 所有主键 ID:string(推荐 UUID/雪花 ID),不可复用
- 金额:服务端/数据库统一使用 BIGINT(分 cents),不可用 float
- 数量:int(>= 0)
- 时间:ISO8601 字符串或毫秒时间戳(两端统一一种)
12.2 核心实体字段(概念级)
Store(门店)
- storeId、name、logoUrl、phone、address、description、createdAt、updatedAt
Table(桌台)
- tableId、storeId、code(桌号,门店内唯一)、status(FREE/OCCUPIED)、createdAt、updatedAt
TableCart(桌台购物车,会话级共享草稿)
- cartId、storeId、tableId、openedAt、version(int,单调递增)、items[]、updatedAt
TableCartItem(桌台购物车明细)
- cartItemId、cartId、skuId、goodId
- goodNameSnapshot、specTextSnapshot、unitPriceSnapshot、qty
- addedByUserId、addedByNicknameSnapshot、addedByAvatarSnapshot
- createdAt、updatedAt
Order(订单)
- orderId、orderNo(展示用)、storeId、tableId
- status(Created/Paid/Making/Completed/Canceled/Refunding/Refunded)
- remark(可选)、paymentMethod(WECHAT/ALIPAY/OTHER,按实现)
- totalAmount、paidAmount、refundedAmount
- createdAt、paidAt(可选)、completedAt(可选)、canceledAt(可选)、refundedAt(可选)
OrderItem(订单明细)
- orderItemId、orderId、skuId、goodId
- goodNameSnapshot、specTextSnapshot、unitPriceSnapshot、qty
- servedQty(int,默认 0)
- addedByUserId、addedByNicknameSnapshot、addedByAvatarSnapshot
12.3 关键约束与一致性规则
- 唯一性:
- Table.code 在 storeId 范围内唯一
- Category.name 在 storeId 范围内唯一
- SKU 在同一 goodId 下按“规格组合”唯一
- 快照:
- 订单与订单明细必须使用下单时快照字段展示,后台改名/改价不影响历史订单
- 并发与幂等:
- TableCart 编辑建议采用 opId 幂等 + version 乐观并发控制
- 创建订单建议采用 clientRequestId 或(tableId + cartVersion)幂等键,避免重复下单
- 上菜与自动流转:
- 0 <= servedQty <= qty
- servedQty 首次从 0 变为 >0 且订单为 Paid 时,订单自动流转 Making(若启用该规则)
- 所有明细 servedQty == qty 时订单自动流转 Completed(若启用该规则)