版本:当前实现 面向系统:BiteGo 点点餐(小程序端) 项目定位:自助点餐系统的小程序端 技术约束:小程序端 Taro + React + TypeScript;服务端 Node.js + Express + TypeScript;前后端分离、REST 接口交互,使用 Yarn 作为包管理器。
1. 背景与定位
点点餐小程序端用于堂食扫码点餐,核心诉求是“少等待、少输入、可协同”。顾客扫描桌台二维码进入点餐页,同桌多名用户可同时加购并共享同一个桌台购物车;下单后可在桌台订单页查看本桌订单进度,并在符合规则时发起退款。
小程序端并非独立系统,其商品模型、订单状态机与后台管理端必须一致:
- 菜品(Good)与商品(SKU)以后台维护为准,上架/库存/价格直接影响前台可售。
- 订单状态机必须与后台一致(创建、支付、制作、完成、取消、退款)。
- 桌台维度的数据(桌台状态、桌台购物车、桌台订单)需要被后台实时可见,以便门店/后厨处理。
2. 产品目标
2.1 用户目标(C 端)
- 扫码即点:顾客扫描桌台二维码进入点餐页,无需复杂注册即可开始加购。
- 协同点餐:多人同桌共同编辑同一个桌台购物车,并能实时看到彼此变更结果。
- 快速下单:提单页确认并提交订单,支付流程清晰明确。
- 可追踪:桌台订单页可查看本桌订单列表与状态变化;个人可在历史订单中查看自己的订单与详情。
2.2 门店目标(B 端联动)
- 后台实时可见:后台可实时查看每个桌台的购物车内容与桌台关联订单,辅助后厨出餐与门店协同。
- 降低误操作:通过状态机约束、幂等与提示,减少重复提交/误退款等风险。
3. 范围说明(当前实现)
3.1 页面范围
- 主页(入口/落地页)
- 扫码点餐入口(唤起扫码)
- 历史订单入口(跳转历史订单页)
- 点餐页(扫码桌台二维码进入)
- 桌号信息展示
- 菜品列表(分类、搜索、排序)
- 购物车数量与金额展示(实时更新)
- 提交订单按钮(打开购物车七分屏或跳转提单页)
- 菜品详情七分屏
- 商品规格七分屏(SKU 选择与数量)
- 购物车七分屏
- 提单页(确认与提交)
- 桌台订单页(本桌多订单聚合)
- 历史订单页(个人维度)
- 历史订单详情页
3.2 必备能力
- 支持多个用户同时点餐,端与端之间的购物车共享(桌台维度共享)。
- Web 管理端能够实时展示每个桌台的购物车内容、关联订单内容等信息。
- 管理员能在后台针对桌台管理订单,也可以针对所有未结算订单进行管理(便于后厨查看需要制作/上菜的订单)。
3.3 明确非范围
- 复杂营销(优惠券、满减、会员积分)
- 外卖配送(骑手/地址/配送费)
- 评价体系与内容社区
4. 角色与身份体系
4.1 C 端角色
- 顾客:参与桌台点餐、提交订单、查看桌台订单与个人历史订单。
4.2 身份与登录
采用微信小程序的用户标识体系,服务端以 openid(或等价标识)识别用户。顾客可在不显式注册账号的情况下完成点餐,但涉及“历史订单”查询与“退款”操作时必须完成授权登录(可采用静默登录 + 首次敏感操作弹授权)。
补充登录流程(业务口径):
- 小程序端调用
wx.login()获取一次性code(有效期 5 分钟)。 - 小程序端调用服务端接口
POST /api/v1/auth/wechat/login,用code换取业务 JWT(role=CUSTOMER),并本地持久化(Authorization: Bearer <token>)。 - 服务端调用微信
jscode2session获取openid/session_key,并将session_key安全存储在服务端(Redis),不下发到客户端。 - 若需要手机号等敏感信息,用户在小程序端触发授权按钮获取
encryptedData/iv,再调用POST /api/v1/users/me/phone由服务端基于session_key解密并绑定。 - 若业务接口返回 401/登录态失效,则触发重新登录(重新调用
wx.login()获取新code并换取新 token)。
5. 核心概念与数据范围
5.1 门店、桌台与扫码进入
- 门店(Store):系统支持单门店与多门店(平台多租户)两种运营形态。
- 桌台(Table):堂食点餐的核心上下文。顾客扫码后进入“桌台会话”。
二维码参数约定(与前端路由规范一致):
- 使用微信“小程序码(不限制数量)”能力生成:
page = pages/order-meal/index。 - 通过
scene传递桌台参数:scene = tableId=<TableId>(长度 ≤ 32,禁止包含敏感信息)。 - 小程序端在启动/页面
onLoad中解析options.scene,通过decodeURIComponent解码后得到tableId。
多门店补充(保持扫码流程不变):
- 二维码仍仅需携带
tableId,不要求在二维码参数中额外携带storeId。 - 服务端通过
tableId反查得到storeId/store信息,并在GET /tables/:tableId等接口中以扩展字段返回,供小程序展示门店信息与进行跨门店历史订单标注。
小程序端路由治理(集中式路由封装、安全回退、扫码参数解析与边界处理)详见 7.3-核心实现-小程序端路由管理.md。
5.2 桌台会话(Table Session)
桌台会话 =(门店 + 桌台)在某一时间段内的点餐协同上下文,包含:
- 桌台基本信息(桌号、状态:空闲/占用)
- 桌台购物车(桌台共享)
- 桌台订单列表(本桌产生的订单聚合)
鉴权与安全(JWT、桌台 sessionToken、同桌订单共享、gtv 全局失效)详见 7.5-核心实现-鉴权与安全.md。
5.3 商品模型对齐(与 Web 端一致)
- 菜品(Good):展示单位
- 规格(SpecGroup/Option):分为“库存规格组/非库存规格组”;仅“库存规格组”参与 SKU 组合生成
- 共享非库存规格组:一种“非库存规格组”,由管理端统一维护并可被多个菜品复用;小程序端与普通非库存规格组一致展示与计价;不展示共享规格组描述字段
- SKU:库存/上架状态的管理单位;前台“可售/库存”判断以 SKU 为准
- 价格项(PI / Price Item):下单/加购的最终计价单元,可关联 SKU + 非库存规格选择 + 营销优惠等;用于记录“非库存规格加价”等导致的最终单价
前台展示时:
- 菜品列表展示 Good;点击后进入详情;选择规格后明确到 SKU 再加入购物车。
- 无规格菜品必须存在默认 SKU;商品规格七分屏中不需要让用户“选规格”,但仍需展示“默认规格/无规格”文案以减少歧义。
6. 关键用户路径(User Journey)
6.1 扫码点餐主流程
- 用户扫描桌台二维码 → 打开小程序并进入点餐页(携带桌台参数)。
- 系统校验桌台有效性 → 拉取门店信息、桌台信息与当前桌台购物车(含桌台会话版本/会话 token)→ 加入桌台实时协同通道。
- 用户浏览菜品列表 → 点击菜品 → 选择规格与数量 → 加入购物车(桌台共享实时更新)。
- 用户打开购物车七分屏 → 确认明细 → 跳转提单页。
- 用户在提单页填写备注(可选)与选择支付方式 → 提交订单 → 发起支付(或模拟支付)。
- 支付成功后进入桌台订单页查看订单状态;若支付失败可回到提单页重试或取消。
6.2 桌台加菜流程
桌台订单页点击“加菜” → 返回点餐页 → 继续在同桌台上下文内加购并提交新订单。
6.3 历史订单流程(个人维度)
主页点击“历史订单” → 订单列表(按时间倒序) → 进入订单详情 → 可按规则申请退款。
7. 页面需求(详细)
7.1 主页(入口/落地页)
信息结构
- 主按钮:扫码点餐(调用扫码能力)
- 次入口:历史订单
- 个人信息展示:
- 展示用户头像与昵称(未设置时提示“点击设置昵称头像”)
- 点击进入个人信息页,可编辑头像与昵称
- 门店信息展示:
- 首次访问(无扫码历史):展示平台信息(平台名称 “BiteGo 点点餐” + 平台 Logo,均可由 Web 管理端配置)
- 有扫码历史(曾成功入桌):展示“上一次扫码进入的门店”信息(门店名称/Logo/地址/电话/描述),并在后续访问中保持展示该门店,直到下一次扫码进入新的门店
- 标题栏:固定显示产品名称“BiteGo 点点餐”
交互
- 点击“扫码点餐”:
- 唤起扫码能力(识别桌台二维码)
- 解析二维码参数并进入点餐页
- 解析失败提示“二维码无效,请重新扫码”
补充:多租户/多门店展示与数据隔离策略详见 7.6-核心实现-平台多租户.md。
7.2 点餐页(扫码桌台进入)
页面目标
让用户在最少操作下完成“找菜—选规格—加购—提单”。
信息结构(建议)
- 顶部:门店名称(可选)+ 桌号信息(例如:A01 桌)
- 搜索栏:按菜品名称搜索
- 顶部区域(桌号 + 搜索)需吸顶,滑动菜品列表时保持可见
- 分类导航:纵向电梯导航(来自分类,按分类排序 sort 值降序:数字越大越靠前)
- 小标签:若分类配置了“小标签”,在分类名称右上角以蓝底小气泡展示
- 排序:默认综合/销量/价格
- 菜品列表(右侧内容区):
- 按分类分组展示(每组有分类标题)
- 副标题:若分类配置了“副标题”,在分类标题右侧以灰色小字展示
- 分类内排序:按“分类-菜品映射”的
sort值降序展示(数值越大越靠前);未设置排序时按创建时间升序兜底
- 点击左侧分类时,右侧自动滚动到对应分组(电梯效果)
- 滚动右侧菜品列表时,左侧分类需自动高亮并滚动到可视区域(分类过多时避免高亮项不在视图内;为保证最终命中,建议对滚动触发的菜单滚动做尾部节流/防抖)
- 搜索状态:当用户输入关键词搜索菜品时,若某分类下无命中结果,则该分类整体隐藏(不展示分类标题,也不在左侧分类导航中展示)
- 菜品图片、名称、简要描述(可选)、起售价、已售/销量(可选)
- 入口:点击进入菜品详情七分屏
- 按分类分组展示(每组有分类标题)
点餐页“分类渲染 + 电梯锚定(左侧菜单与右侧分区双向联动)”的具体实现方案与稳定性兜底详见 7.4-核心实现-小程序端点餐页分类渲染.md。
- 底部悬浮条:
- 购物车图标 + 已选数量(实时)
- 总金额(实时)
- “去结算/提交订单”按钮
可售规则
- 仅可售存在“至少一个上架且库存可用 SKU”的菜品。
- 若菜品本身为上架状态,但全部 SKU 已下架或库存为 0(即“售罄”):
- 商品名称与起售价置灰,菜品卡片整体不可点击
- 一句话描述隐藏,在菜品名称右侧用灰色小标签标注“已售罄”
- 在所属分类内排序放到最后(已售罄菜品之间仍按原有规则排序)
桌台校验(必做)
- 进入点餐页前必须校验桌台存在且可用;若桌台不存在或会话不可用,提示“桌台不存在/已关闭”,并引导返回首页重新扫码。
7.3 菜品详情七分屏
展示内容
- 菜品图片(支持多图轮播)
- 菜品名称
- 菜品描述(一句话描述)
- 菜品详情(Markdown 富文本,展示更详细说明)
- 起售价/价格区间(根据 SKU 价格)
- 规格摘要(例如“可选杯型、甜度”或“无规格”)
- 操作按钮:选购
交互
- 图片支持左右滑动切换;点击图片可打开灯箱预览并左右切换。
- 点击“选购” → 打开“商品规格七分屏”
- 向下滑/点击遮罩 → 关闭七分屏
7.4 商品规格七分屏(SKU 选择与数量)
目标
明确到 SKU(由“库存规格组”选择决定),并生成 PI(叠加“非库存规格组”加价),完成“规格选择 + 数量选择 + 加购”。
展示内容
- 菜品信息(缩略图、名称)
- 规格组选择区域(同时展示库存规格组与非库存规格组):
- 每个规格组以标签/按钮展示规格值;规格值若存在加价(priceCents > 0),在名称后用加粗文本展示“¥x”
- 规格组标题右侧展示灰色小字规则提示:可选/最少 x 个/最多 x 个(命中多条规则时用中文逗号拼接)
- 共享非库存规格组与普通非库存规格组一致展示与交互,仅在数据侧通过
groupType=shared/sharedSpecGroupId标识来源 - 可选规格组交互:对“可不选任何规格”的规格组(minSelection=0),再次点击已选规格可取消选择
- 默认选中规则:
- 以规格组配置的
defaultOptionIds为准:若配置了默认选项,则按默认选项自动选中 - 单选规格组:最多选中 1 个默认选项;多选规格组:最多选中
maxSelection个默认选项 - 未配置默认选项的规格组不自动选中(必选组需用户手动选择后才能加购)
- 以规格组配置的
- 数量区域:- / + 按钮 + 数量输入(可按体验取舍是否提供手输,需避免误触)
- 价格展示:展示 PI 单价与总金额(PI 单价 = SKU 单价 + 非库存规格加价)
- 操作按钮:
- 取消选择/清空(可选)
- 加入购物车
校验规则
- 必须完成所有必选规格组选择后才能加入购物车(可选组可不选)。
- 库存校验:
- 规格选择阶段:若某库存规格组合对应 SKU 已售罄/不可售,则禁用该组合相关选项,避免选到缺货 SKU
- 加购阶段(二次校验):若 SKU 库存不足(含购物车内已占用数量),禁止加购并提示“库存不足”
- 提单阶段(三次校验):提交订单前再次拉取最新库存并校验,避免多人并发导致超售;最终仍以服务端校验为准
加购结果
- 加购成功:关闭规格七分屏;点餐页购物车数量与金额实时更新
- 加购失败:展示失败原因(例如下架、无库存、桌台已结束/已重置)
7.5 购物车七分屏(桌台共享)
展示内容
- 标题:桌台购物车(A01 桌)
- 列表项:
- 菜品名称(快照)
- 规格文本(如“中杯/不加糖”)
- 单价
- 数量(- / +)
- 小计
- 删除按钮
- 底部:
- 总金额(需吸附在七分屏底部,确保长列表滚动时也可见)
- “去提单/提交订单”按钮(跳转提单页)
多人协同展示
- 购物车顶部提示:“同桌多人可同时点餐,购物车将实时同步”
- 订单内商品添加人提示(必做,见 7.5.4/7.7/7.9)
修改规则
- 数量增减、删除均为“桌台共享操作”,所有端实时同步。
- 对已下单的订单不再从购物车回写;购物车仅代表“待下单”的草稿集合。
7.5.1 完整功能流程(添加 / 删除 / 修改 / 结算)
购物车是桌台维度的共享草稿,需覆盖以下完整流程:
- 添加(加入购物车)
- 触发点:商品规格七分屏点击“加入购物车”
- 入参:skuId、qty、tableSession(storeId+tableId)、addedBy(当前用户)
- 结果:
- 若购物车中存在“相同 skuId 且相同 addedByUserId”的条目,则 qty 累加
- 否则新增一条购物车条目
- 删除
- 触发点:购物车七分屏点击“删除”
- 结果:删除该条购物车条目(不影响已生成订单)
- 修改
- 数量加减:- / + 触发
- 数量变为 0:等价删除(需二次确认可选)
- 结算/去提单
- 触发点:底部“去提单/提交订单”
- 行为:进入提单页,提单页可继续修改购物车明细
7.5.2 数据交互规则(服务端权威)
- 客户端所有改动均提交为“操作(Operation)”,由服务端落库并广播给同桌其他端(与 9.3 一致)。
- UI 更新策略:
- 推荐:提交操作后进行乐观更新,同时等待服务端回包/推送确认;若失败则回滚并刷新最新购物车。
- 操作失败典型原因:SKU 下架、库存不足、桌台会话已结束/被重置、版本冲突。
- 金额计算口径:
- 页面展示金额以“服务端回传的 cartTotalAmount”为准;
- 若需前端即时计算,仅用于临时展示,最终以下单时服务端计算为准。
- 价格变化口径:
- 若后台调整 SKU 价格,购物车需在服务端刷新后展示最新单价;
- 提单时若发生价格变化,需提示“价格已更新,请确认后提交”并展示差异。
7.5.3 结算与清空规则
- 提交订单成功后:
- 订单创建成功后清空桌台购物车(默认整单清空)
- 若存在并发加购导致“提交时购物车已变化”,服务端以 cartVersion 幂等键为准:
- 若 cartVersion 不一致,返回“购物车已更新”并要求用户重新确认
- 若存在并发加购导致“提交时购物车已变化”,服务端以 cartVersion 幂等键为准:
7.5.4 订单内商品添加人提示(必做)
目标
多人同桌协同点餐时,需要在购物车与订单中明确“谁添加了这份商品”,便于沟通与核对。
数据来源
- 购物车阶段:TableCartItem.addedByUserId + addedByNicknameSnapshot + addedByAvatarSnapshot
- 下单阶段:OrderItem 继承 TableCartItem 的 addedBy 快照字段
- 用户头像/昵称:用户资料(后端持久化),在桌台协同 WebSocket 添加购物车项时写入快照字段;未设置则展示为“匿名”(并提示用户可去设置)
展示规则
- 购物车七分屏:每条购物车项左侧展示添加人头像(或昵称首字),并在规格行后追加“由xx添加”
- 桌台订单页/历史订单详情页:
- 每条订单明细展示“由xx添加”
- 展示策略采用业界主流的“按 SKU 聚合 + 添加人维度可识别”口径:
- 数据层:按
skuId + addedByUserId维度保留归属(避免不同人加的同款被合并后丢失归属) - UI 层:可按 SKU 聚合展示为一行,并在明细/展开区域展示“各添加人分别添加的数量”
- 数据层:按
- 若同一 SKU 同一添加人多次添加,合并数量展示(qty 汇总)
- 加购引导:若用户昵称或头像未设置,点击“加入购物车”时弹窗引导去设置;用户可选择继续使用“匿名”
7.6 提单页(确认与提交)
展示内容
- 桌号信息
- 购物车明细(与购物车七分屏一致,允许继续调整数量/删除):
- 菜品名称、规格明细、数量、添加人、单价、小计
- 订单备注输入框(可选,0–100 字)
- 支付方式选择(微信、支付宝)
- 总金额
- “提交订单”按钮
交互补充:
- “备注”与“提交订单”区域吸附在页面底部,便于长列表场景下快速提交。
支付方式说明(产品口径)
- 微信支付:默认选中;当前实现采用“模拟支付”,提交订单后直接模拟支付成功并流转订单状态。
- 支付宝:微信小程序内无法直接拉起支付宝原生支付;产品上保留入口,落地方式二选一:
- 方案 A(推荐):标记为“暂不支持”,置灰并提示“请使用微信支付”
- 方案 B:跳转 H5 收银台(需要额外建设)
默认采用方案 A,保证流程可交付。
提交订单规则
- 提交时服务端必须再次校验:
- 购物车是否为空
- SKU 是否上架
- SKU 库存是否满足
- 桌台是否有效且处于可点餐状态
- 成功后:
- 创建订单(服务端可能短暂处于
Created) - 模拟支付:创建后立即模拟支付成功并将状态更新为
Paid,前端默认不展示“待支付” - 清空桌台购物车(或仅清空已下单部分;当前实现为整单提交后清空购物车)
- 创建订单(服务端可能短暂处于
- 失败处理:
- 库存不足:提示具体 SKU,并给出“回到购物车修改”入口
- 网络失败:提示重试,确保接口幂等(重复点击不会生成多单)
7.7 桌台订单页(桌台维度聚合)
展示目标
提供“本桌订单进度视图”,让顾客知道“已下单/已支付/制作中/已完成/已取消/已退款”等状态,并支持加菜与退款(按规则)。
展示结构
- 顶部:
- 桌号信息
- 桌台总订单金额加总(统计本桌所有“非取消/非退款”订单的实付或应付口径,需统一规则)
- 订单列表(按创建时间倒序或按未完成优先):
- 订单号(短号展示即可)
- 状态标签(见 8.2)
- 商品列表(菜品、规格、数量、添加人提示、上菜进度)
- 订单金额
- 操作按钮:
- 申请退款(满足条件才展示/可点)
- 底部:
- 底部吸附栏(固定在页面底部):
- 加菜/返回点餐按钮(跳转点餐页)
- 本桌下单汇总:菜品总数与总价(退款不计入,建议同时排除取消口径以对齐顶部规则)
- 底部吸附栏(固定在页面底部):
7.8 历史订单页(用户维度,支持跨门店)
列表字段
- 订单号
- 门店信息(必做):门店名称 + Logo(用于区分不同门店下的订单)
- 桌台信息:桌号(优先)+ 桌台ID(可在详情页查看)
- 状态
- 总金额
- 创建时间
- 操作:查看详情、申请退款(按规则)
筛选(可选)
- 状态筛选
- 时间范围
- 门店筛选(可选):当用户历史订单跨多个门店时,支持按门店筛选
7.9 历史订单详情页
- 订单状态展示
- 桌台信息:桌台号与桌台ID
- 时间节点:下单时间、支付时间、完成时间(如有)、取消/退款时间(如有)
- 订单商品明细(菜品、规格、数量、单价、小计、添加人提示、上菜进度)
- 订单总金额
- 支付信息(可选)
- 申请退款按钮(按规则)
7.9.1 退款规则(必做)
- 可退款条件:
- 仅允许对状态为
Paid的订单申请退款 Making/Completed不允许退款;Refunded/Canceled不允许重复申请
- 仅允许对状态为
- 交互与提示:
- 点击“申请退款”后订单状态立即进入“退款中(Refunding)”,按钮置灰
- 退款中需要后台审核:
- 审核通过:退款处理完成后订单状态变更为
Refunded - 审核不通过:订单状态回退为
Paid,用户可重新发起退款申请(以服务端退款流水记录为准)
- 审核通过:退款处理完成后订单状态变更为
- 若用户关闭页面再次进入,仍应展示“退款中/已退款/已支付”的正确结果(以服务端状态为准)
- 服务端口径:
- 当前实现采用“模拟退款 + 审核”:服务端创建退款流水进入
REVIEWING,订单状态更新为Refunding - Web 管理端审核通过后,退款流水进入
PENDING;10 秒后自动完成退款:退款流水更新为SUCCESS,订单状态流转为Refunded - 退款完成后服务端会回退 SKU 库存占用,确保用户退款后可再次购买
- 当前实现采用“模拟退款 + 审核”:服务端创建退款流水进入
7.10 桌台浮窗(全局悬浮入口)
目标
在用户处于“桌台会话”期间,为用户提供跨页面的快速入口与状态感知,减少来回跳转成本。
触发条件
- 用户通过扫码进入点餐页并完成桌台会话初始化后自动展示
- 若用户从主页进入但未绑定桌台(未扫码),不展示浮窗
展示内容(最小集)
- 桌号(例如:A01)
- 购物车角标数量(实时)
- 购物车总金额(实时,可选)
- 快捷入口按钮:
- 打开购物车七分屏
- 打开桌台订单页
交互方式
- 浮窗可拖拽移动(可选,可固定在右下角)
- 点击浮窗打开快捷面板(二级浮层):
- 展示“购物车/桌台订单”两个按钮
- 关闭机制:
- 用户可“最小化”为仅保留角标的小气泡(可选)
- 当桌台会话结束(清台/会话失效/用户主动退出桌台)时自动隐藏
桌台被强制清台的处理(必做)
- 当后台执行“强制清台”后:
- 小程序端应收到实时事件或接口错误提示,识别为“桌台会话已关闭”
- 立即展示“桌台已关闭”提示页(或全屏弹窗),引导用户返回主页并重新扫码入桌
- 桌台浮窗自动隐藏,禁止继续加购/提单/查看该桌台订单
与实时同步关系
- 浮窗数据来源与点餐页底部购物车条一致,均来自 TableCart 的实时推送
- 当后台重置桌台购物车或订单状态变更时,浮窗角标与入口状态同步更新
7.11 个人信息页(头像昵称)
目标
在桌台协同点餐场景下,用户可设置头像与昵称,避免在“添加人”展示中默认显示为“匿名”,提升同桌识别与沟通效率。
入口
- 主页个人信息展示区域点击头像或昵称进入
- 加购时若用户未设置昵称或头像,弹窗提示可进入设置(允许继续匿名)
页面能力(必做)
- 头像设置:点击头像按钮,使用微信推荐的
chooseAvatar能力选择头像;若为自定义头像,前端上传到文件上传接口得到静态链接后保存 - 昵称设置:使用微信推荐的
type="nickname"输入框,用户手动填写昵称 - 保存:保存成功后返回上一页,并使后续加购/订单明细中的“添加人”展示使用新的头像与昵称
8. 订单与状态机(对齐 Web 端)
8.1 状态定义(系统状态)
- 创建(Created)
- 支付(Paid)
- 制作(Making)
- 完成(Completed)
- 取消(Canceled)
- 退款中(Refunding)
- 退款(Refunded)
8.2 状态展示文案(面向用户)
建议映射:
- Created:已创建(默认不展示该中间态)
- Paid:已支付
- Making:制作中
- Completed:已完成
- Canceled:已取消
- Refunding:退款中
- Refunded:已退款
8.3 状态流转(建议)
- Created → Paid → Making → Completed
- Created → Canceled
- Paid → Refunding → Refunded(申请退款 → 审核通过并退款完成)
- Refunding → Paid(审核不通过)
说明:
- 支付后不直接允许进入 Canceled,统一通过 Refunded 表达(减少歧义)。
- 小程序端的“申请退款”为用户发起的请求,最终是否退款成功以服务端处理结果为准。
- 上菜驱动的自动流转(若启用与 Web 后厨工作台一致的规则):
- 当订单首次发生上菜(任一 OrderItem.servedQty 从 0 变为 >0)且订单状态为 Paid,则订单自动流转为 Making。
- 当订单内所有 OrderItem 满足 servedQty == qty,则订单自动流转为 Completed,并在小程序端展示为“已上菜/已完成”。
9. 多用户共享购物车与实时同步(核心要求)
9.1 共享粒度
共享粒度为“桌台购物车”:
- 同一桌台下的所有用户共享同一个购物车草稿。
- 任一用户的加购、删改数量等操作,会实时同步到其他在桌的用户端。
9.2 同步目标
- 低延迟:关键操作 1 秒内可见(目标值)
- 一致性:最终以服务端为准,客户端支持冲突合并
- 幂等性:重复提交同一操作不会产生多次叠加
9.3 推荐实现机制(需求口径,不限定技术)
推荐采用“服务端权威 + WebSocket 推送 + 版本号”的协同模型:
- 服务端维护 TableCart:
- cartId、tableId、items、version、updatedAt
- 客户端以“操作(Operation)”提交变更:
- opId(全局唯一)、clientId、baseVersion、opType、payload
- 服务端处理后:
- 成功:version + 1,并广播最新 cart(或增量变更)给该桌台的所有订阅端
- 失败:返回错误码与最新 cart(用于客户端回滚/刷新)
Web 管理端同样可订阅桌台 cart 与订单变更,以满足“后台实时展示桌台购物车内容”的要求。
9.4 冲突与并发场景(必须覆盖)
- 两个用户同时对同一 SKU +1:
- 服务端按顺序处理 op;最终数量为 +2(在库存允许范围内)
- 库存不足:
- 服务端拒绝导致超库存的 op,并返回“库存不足 + 当前可用库存/当前 cart”
- 客户端离线后恢复:
- 重连时拉取最新 cart;本地未提交的 op 可选择丢弃或重放(当前实现建议丢弃并提示“已为你刷新最新购物车”)
- 重复点击:
- opId 幂等;服务端检测重复 opId 直接返回上次结果
10. 与 Web 管理端的联动需求
10.1 桌台购物车可视化
Web 管理端需能查看:
- 每个桌台当前 cart(SKU 明细、数量、总金额、更新时间)
- 关联订单列表(Created/Paid/Making/Completed/Refunded/Canceled)
小程序端的 cart 与订单变更需在后台实时可见(允许 1–3 秒延迟)。
10.2 后厨/门店处理订单
后台可对订单进行状态变更(Paid→Making→Completed、Paid→Refunded 等),状态变更需要同步到桌台订单页与历史订单详情页。
11. 概念数据模型(对齐后端)
- Store:门店
- Table:桌台
- Category:分类
- Good:菜品
- SpecGroup / SpecOption:规格
- SKU:商品
- TableCart:桌台购物车(共享,会话级草稿,包含 openedAt/version)
- TableCartItem:购物车明细(skuId、goodId、goodNameSnapshot、specTextSnapshot、unitPriceSnapshot、qty、addedByUserId、addedByNicknameSnapshot、addedByAvatarSnapshot)
- Order:订单
- OrderItem:订单明细(快照字段,包含 servedQty 与 addedBy)
快照字段要求:
- 下单时必须保存 goodName/specText/unitPrice 等快照,避免后台改名/改价影响历史订单展示。
12. 非功能性需求
12.1 体验
- 页面首屏加载:点餐页在弱网下也应给出骨架屏/加载态与可恢复提示。
- 减少输入:尽量采用选择与按钮操作,备注为可选。
- 容错提示:库存不足、下架、桌台无效、网络错误均需有明确文案与可执行动作。
12.2 安全
- 桌台二维码参数防篡改:二维码携带签名 token(服务端校验),避免随意伪造 tableId。
- 关键操作(提交订单、退款申请)必须鉴权并校验用户身份。
12.3 幂等与稳定性
- 提交订单接口必须幂等:同一桌台同一购物车版本在短时间内重复提交,不应产生重复订单。
- 退款申请必须幂等:重复申请不会生成多条退款记录。
13. 验收清单
13.1 页面与流程
- 主页可扫码进入点餐页,可进入历史订单页。
- 点餐页可按分类浏览、搜索菜品;购物车数量与金额实时更新。
- 菜品详情七分屏可打开;规格七分屏可选规格与数量并加入购物车。
- 购物车七分屏可删改 SKU 数量并跳转提单页。
- 提单页可填写备注、提交订单;支付方式展示符合约束(微信可用,支付宝按当前规则处理)。
- 桌台订单页可查看本桌多订单与状态;可加菜;可按规则申请退款。
- 历史订单页与详情页可查看个人订单,支持按规则申请退款。
13.2 多端协同
- 两个用户同时在同一桌台加购,购物车在两端实时同步且最终一致。
- 后台可实时看到桌台购物车与桌台订单变化。
- 后台变更订单状态后,小程序端桌台订单页与历史订单详情页同步更新。
13.3 异常与容错
- 库存不足、SKU 下架、桌台无效、网络失败均有明确提示与可恢复操作。
- 重复点击提交订单不会生成重复订单(幂等校验通过)。
14. 迭代建议(可选)
- 展示“添加人”与协同提示(谁加了什么)
- 已售罄灰态展示与到货提醒
- 桌台结账/清台能力(与桌台状态更紧密联动)
- 支付宝/H5 收银台、多门店、多角色权限
15. 附录A:统一数据模型标准(Web/小程序共用)
本附录用于对齐 Web 管理端与小程序端在数据模型层的字段口径、约束与关系结构,作为两端 PRD 的共同标准。若后续迭代新增字段,两端 PRD 需同时更新本附录并保持一致。
15.1 标识与字段类型约定
- 所有主键 ID:string(推荐 UUID/雪花 ID),不可复用
- 金额:服务端/数据库统一使用 BIGINT(分 cents),不可用 float
- 数量:int(>= 0)
- 时间:ISO8601 字符串或毫秒时间戳(两端统一一种)
15.2 核心实体字段(概念级)
Store(门店)
- storeId、name、logoUrl、phone、address、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
15.3 关键约束与一致性规则
- 唯一性:
- Table.code 在 storeId 范围内唯一
- Category.name 在 storeId 范围内唯一
- SKU 在同一 goodId 下按“规格组合”唯一
- 快照:
- 订单与订单明细必须使用下单时快照字段展示,后台改名/改价不影响历史订单
- 并发与幂等:
- TableCart 编辑建议采用 opId 幂等 + version 乐观并发控制
- 创建订单建议采用 clientRequestId 或(tableId + cartVersion)幂等键,避免重复下单
- 添加人与归属:
- 若前台需要展示“添加人”,TableCartItem 与 OrderItem 均必须保留 addedBy 的快照字段
- 购物车明细的聚合键至少包含 skuId + addedByUserId(避免不同人加的同款被合并后丢失归属)
- 上菜与自动流转:
- 0 <= servedQty <= qty
- servedQty 首次从 0 变为 >0 且订单为 Paid 时,订单自动流转 Making(若启用该规则)
- 所有明细 servedQty == qty 时订单自动流转 Completed(若启用该规则)