#3.2

点点餐 小程序端 产品需求文档(PRD)

约 8.8 千字 · 在 GitHub 上查看源码

版本:当前实现 面向系统:BiteGo 点点餐(小程序端) 项目定位:自助点餐系统的小程序端 技术约束:小程序端 Taro + React + TypeScript;服务端 Node.js + Express + TypeScript;前后端分离、REST 接口交互,使用 Yarn 作为包管理器。


1. 背景与定位

点点餐小程序端用于堂食扫码点餐,核心诉求是“少等待、少输入、可协同”。顾客扫描桌台二维码进入点餐页,同桌多名用户可同时加购并共享同一个桌台购物车;下单后可在桌台订单页查看本桌订单进度,并在符合规则时发起退款。

小程序端并非独立系统,其商品模型、订单状态机与后台管理端必须一致:

  • 菜品(Good)与商品(SKU)以后台维护为准,上架/库存/价格直接影响前台可售。
  • 订单状态机必须与后台一致(创建、支付、制作、完成、取消、退款)。
  • 桌台维度的数据(桌台状态、桌台购物车、桌台订单)需要被后台实时可见,以便门店/后厨处理。

2. 产品目标

2.1 用户目标(C 端)

  • 扫码即点:顾客扫描桌台二维码进入点餐页,无需复杂注册即可开始加购。
  • 协同点餐:多人同桌共同编辑同一个桌台购物车,并能实时看到彼此变更结果。
  • 快速下单:提单页确认并提交订单,支付流程清晰明确。
  • 可追踪:桌台订单页可查看本桌订单列表与状态变化;个人可在历史订单中查看自己的订单与详情。

2.2 门店目标(B 端联动)

  • 后台实时可见:后台可实时查看每个桌台的购物车内容与桌台关联订单,辅助后厨出餐与门店协同。
  • 降低误操作:通过状态机约束、幂等与提示,减少重复提交/误退款等风险。

3. 范围说明(当前实现)

3.1 页面范围

  1. 主页(入口/落地页)
    • 扫码点餐入口(唤起扫码)
    • 历史订单入口(跳转历史订单页)
  2. 点餐页(扫码桌台二维码进入)
    • 桌号信息展示
    • 菜品列表(分类、搜索、排序)
    • 购物车数量与金额展示(实时更新)
    • 提交订单按钮(打开购物车七分屏或跳转提单页)
  3. 菜品详情七分屏
  4. 商品规格七分屏(SKU 选择与数量)
  5. 购物车七分屏
  6. 提单页(确认与提交)
  7. 桌台订单页(本桌多订单聚合)
  8. 历史订单页(个人维度)
  9. 历史订单详情页

3.2 必备能力

  1. 支持多个用户同时点餐,端与端之间的购物车共享(桌台维度共享)。
  2. Web 管理端能够实时展示每个桌台的购物车内容、关联订单内容等信息。
  3. 管理员能在后台针对桌台管理订单,也可以针对所有未结算订单进行管理(便于后厨查看需要制作/上菜的订单)。

3.3 明确非范围

  • 复杂营销(优惠券、满减、会员积分)
  • 外卖配送(骑手/地址/配送费)
  • 评价体系与内容社区

4. 角色与身份体系

4.1 C 端角色

  • 顾客:参与桌台点餐、提交订单、查看桌台订单与个人历史订单。

4.2 身份与登录

采用微信小程序的用户标识体系,服务端以 openid(或等价标识)识别用户。顾客可在不显式注册账号的情况下完成点餐,但涉及“历史订单”查询与“退款”操作时必须完成授权登录(可采用静默登录 + 首次敏感操作弹授权)。

补充登录流程(业务口径):

  1. 小程序端调用 wx.login() 获取一次性 code(有效期 5 分钟)。
  2. 小程序端调用服务端接口 POST /api/v1/auth/wechat/login,用 code 换取业务 JWT(role=CUSTOMER),并本地持久化(Authorization: Bearer <token>)。
  3. 服务端调用微信 jscode2session 获取 openid/session_key,并将 session_key 安全存储在服务端(Redis),不下发到客户端。
  4. 若需要手机号等敏感信息,用户在小程序端触发授权按钮获取 encryptedData/iv,再调用 POST /api/v1/users/me/phone 由服务端基于 session_key 解密并绑定。
  5. 若业务接口返回 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 扫码点餐主流程

  1. 用户扫描桌台二维码 → 打开小程序并进入点餐页(携带桌台参数)。
  2. 系统校验桌台有效性 → 拉取门店信息、桌台信息与当前桌台购物车(含桌台会话版本/会话 token)→ 加入桌台实时协同通道。
  3. 用户浏览菜品列表 → 点击菜品 → 选择规格与数量 → 加入购物车(桌台共享实时更新)。
  4. 用户打开购物车七分屏 → 确认明细 → 跳转提单页。
  5. 用户在提单页填写备注(可选)与选择支付方式 → 提交订单 → 发起支付(或模拟支付)。
  6. 支付成功后进入桌台订单页查看订单状态;若支付失败可回到提单页重试或取消。

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 完整功能流程(添加 / 删除 / 修改 / 结算)

购物车是桌台维度的共享草稿,需覆盖以下完整流程:

  1. 添加(加入购物车)
    • 触发点:商品规格七分屏点击“加入购物车”
    • 入参:skuId、qty、tableSession(storeId+tableId)、addedBy(当前用户)
    • 结果:
      • 若购物车中存在“相同 skuId 且相同 addedByUserId”的条目,则 qty 累加
      • 否则新增一条购物车条目
  2. 删除
    • 触发点:购物车七分屏点击“删除”
    • 结果:删除该条购物车条目(不影响已生成订单)
  3. 修改
    • 数量加减:- / + 触发
    • 数量变为 0:等价删除(需二次确认可选)
  4. 结算/去提单
    • 触发点:底部“去提单/提交订单”
    • 行为:进入提单页,提单页可继续修改购物车明细

7.5.2 数据交互规则(服务端权威)

  • 客户端所有改动均提交为“操作(Operation)”,由服务端落库并广播给同桌其他端(与 9.3 一致)。
  • UI 更新策略:
    • 推荐:提交操作后进行乐观更新,同时等待服务端回包/推送确认;若失败则回滚并刷新最新购物车。
    • 操作失败典型原因:SKU 下架、库存不足、桌台会话已结束/被重置、版本冲突。
  • 金额计算口径:
    • 页面展示金额以“服务端回传的 cartTotalAmount”为准;
    • 若需前端即时计算,仅用于临时展示,最终以下单时服务端计算为准。
  • 价格变化口径:
    • 若后台调整 SKU 价格,购物车需在服务端刷新后展示最新单价;
    • 提单时若发生价格变化,需提示“价格已更新,请确认后提交”并展示差异。

7.5.3 结算与清空规则

  • 提交订单成功后:
  • 订单创建成功后清空桌台购物车(默认整单清空)
    • 若存在并发加购导致“提交时购物车已变化”,服务端以 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(若启用该规则)