智慧营养健康餐厅 · 技术评审

PC 消费订单详情与多菜品展示

前后端详情合同、四类消费模式、六菜品可视区、逐菜品异步 XLSX、权限与历史快照的完整开发设计。

VWCG-1334V0.1DEFINE / PLANDoR BLOCKED · 4项待确认
当前结论:技术文档已形成,尚未进入业务代码开发。普通自提/档口历史快照、订单类型与来源映射、虚拟菜品导出口径、超单 Sheet 行数策略需先确认。

VWCG-1334 PC 消费订单详情与多菜品展示技术开发设计

1. 文档信息

内容
云效需求 VWCG-1334 PC端消费订单详情信息与多菜品展示优化
文档版本 V0.1 技术评审稿
日期 2026-09-08
阶段 DEFINE / PLAN
目标仓库 store;若要求新增普通自提/档口快照,需联动全部订单生产端,至少包括 ai_api
当前基线 store origin/master@a7ee68bd830acfbff747ce9655f1a1215957ddae
DoR BLOCKED:4 个 P0 口径待确认,见第 15 节
实施状态 仅完成需求与技术分析;未改业务代码、SQL、云效状态或云效正文

2. 结论先行

这是一个真实需求,但不是“只调弹框样式”的纯前端任务。推荐拆成两个可独立验收、顺序交付的 Use Case:

  1. UC-MEAL-ORDER-DETAIL-001:详情接口生成稳定的展示 DTO,前端用配置驱动的三列栅格展示有效字段;移除菜品表 250px 内部滚动,按 1366×768、100% 缩放验证至少 6 个菜品直接可见。
  2. UC-MEAL-ORDER-EXPORT-002:保留现有异步 .xlsx 和列表筛选/权限范围,以订单游标批量取数、批量查询菜品,一条菜品明细写一行,订单字段重复,合计只按订单累计一次。

当前系统已经具备大部分取餐字段和流式 XLSX 基础,不需要重建页面或更换导出机制。真正的缺口是:

需求优先级建议为 P0。原因不是视觉,而是订单追溯、历史快照和导出口径必须保持一致。

3. 分析框架与真需求判断

本文用三种思路:

判断
表层诉求 增加字段、统一三列、显示 6 个菜品、扩充导出
真实目标 让后台运营能在一个入口核对订单人、组织、档口、取餐、菜品和金额,并导出同口径追溯证据
不做代价 详情继续缺字段;历史人员信息可能随当前资料变化;多菜品导出无法逐项核对
错误方案 仅扩大弹框或把多个菜品名称拼在一个单元格;都不能解决字段合同和逐菜品追溯
结论 真需求;技术上是中等复杂度的前后端 + 异步导出改造

4. 范围与非目标

4.1 In Scope

4.2 Out of Scope

5. 当前系统证据与差距

链路 当前事实 差距 / 处理
PC 弹框 consume/index.vue 中字段按行硬编码 改为字段配置数组 + 三列 CSS Grid;空值过滤后自动补位
弹框尺寸 el-dialog 未指定宽高 建议宽度 80vw、最大宽度约 1200px、顶部 4vh;以原型和实测再微调
菜品滚动 el-table max-height="250",图片 90×90 移除表格固定 250px;图片压到 56×56 或经视觉确认的等效尺寸;滚动归弹框 body
用户/手机 页面读 orderDetail.user.name/mobile,为当前用户资料 后端返回快照优先的展示字段:订单快照非空优先,当前用户资料只作兼容回退
原/当前部门 Logic 已返回 department_attribution 原部门用订单快照;当前部门明确为实时组织关系,不承诺历史不变
档口 订单只有 restaurant_id,列表/打印实时查餐厅 无名称快照;需确认采用实时回退还是新增快照字段
普通自提地址 可从当前档口 location 查询 没有订单地址快照;不满足“配置变化后历史不变”的强验收
取餐方式 restaurant_method 已有 1/2/4 详情和导出需统一中文 堂食/自提/取餐柜,非法值输出空
取餐号 独立表 meal_order_pickup.pickup_no 详情与导出需批量关联;禁止逐订单 N+1 查询
取餐柜 主表已有编号、地址快照、状态、格口和时间 增加中文状态和默认时间清洗;名称只能实时查设备,编号可直接用快照
订单状态 列表已有统一状态映射,详情由前端根据列表行临时推断 将统一状态文本放入详情 DTO,避免列表行过期或算法不一致
金额 列表已有订单金额、优惠、计算后实付 详情复用相同金额函数,统一两位小数;不能直接混用 total_price/pay_price
导出 exportOrdersToFile 以订单为一行、ID 游标每批 1000、流式写 XLSX 增加批量菜品查询与逐菜品写行;订单合计只累计一次
权限 详情有对象级校验;提交导出时收窄部门/餐厅,执行时重放餐厅权限 不改变订单集合;建议补回归测试证明新增 join 不扩大范围

6. 总体设计

flowchart LR
    A["消费订单列表"] -->|"查看 orderId"| B["/p/mealOrder/detail"]
    B --> C["订单主表与用户兼容回退"]
    B --> D["档口 / 取餐号 / 取餐柜字段"]
    B --> E["菜品明细"]
    C --> F["详情展示 DTO"]
    D --> F
    E --> F
    F --> G["配置驱动三列弹框"]

    A -->|"按当前筛选导出"| H["/p/mealOrder/export type=1"]
    H --> I["export_log"]
    I --> J["ExportMealOrder"]
    J --> K["订单游标批次"]
    K --> L["批量查询菜品并按 order_id 分组"]
    L --> M["XlsxStreamWriter 逐菜品写行"]

设计原则:

  1. 不改变现有详情路由和权限入口,只增加兼容字段。
  2. 页面只消费后端给出的稳定展示值,不在前端复制业务枚举和金额算法。
  3. 详情与导出共用字段标准化方法,避免两个口径再次分叉。
  4. 导出继续流式写入;批量查菜品,不把全量订单或全量输出行放进内存。
  5. 所有新增字段为空时输出空字符串,页面不占位,Excel 不出现内部值。

7. UC-MEAL-ORDER-DETAIL-001:查看完整订单详情

7.1 概要

7.2 主流程

步骤 动作 / 系统响应 页面 / API AC
M1 管理员在列表点击查看 PAGE-ORDER-DETAIL-01 AC-D01
M2 前端清空上一个订单状态、打开加载态并请求当前 orderId /p/mealOrder/detail AC-D02
M3 后端校验登录、菜单、餐厅和人员数据权限 Controller 既有校验 AC-D03
M4 后端读取订单、菜品、取餐号、档口及取餐柜相关字段并生成展示 DTO Logic AC-D04
M5 前端过滤空值,按字段顺序自动排成三列;金额独立一行 Vue AC-D05
M6 六个菜品直接可见;更多菜品由弹框内容区整体滚动 Vue/CSS AC-D06

7.3 分支、异常与恢复

ID 条件 行为 恢复
A1@M4 堂食,无取餐字段 隐藏取餐号、时段、地址和柜机字段,后续字段自动补位 正常结束
A2@M4 线上自提 展示取餐方式、取餐号、日期、时段和地址 正常结束
A3@M4 取餐柜 展示编号、快照地址、格口、中文取餐状态、入柜/实际取餐时间 正常结束
A4@M4 外部/其他订单字段缺失 只展示可证明的有效字段,不填 -- 占格 正常结束
E1@M3 无权限或订单越权 后端拒绝,不返回订单内容 R1 关闭弹框或选择有权限订单
E2@M4 订单不存在/已删除 返回业务失败,前端显示错误并停止渲染 R2 刷新列表后重试
E3@M2 网络失败、超时或快速切换订单 保留当前请求序号防止旧响应覆盖新订单 R3 重试当前订单;不得展示上一单数据
E4@M4 枚举非法或默认时间 2000-01-01... 输出空字符串,不向用户显示内部值 R4 记录测试缺口,数据治理另行处理

7.4 业务规则

8. 详情接口设计

8.1 兼容策略

保留现有 data 原字段和 goods 结构,新增 display 对象;旧前端仍可运行,新前端逐步切换到 display。不删除或改名既有字段。

建议响应增量:

{
  "display": {
    "user_name": "张三",
    "user_mobile": "138****0000",
    "order_status_text": "已完成",
    "order_no": "260907194900001",
    "order_type_text": "在线订单",
    "order_source_text": "AI运动营养师订餐订单",
    "trade_time": "2026-09-07 19:49:14",
    "original_department_name": "校本部",
    "current_department_name": "校本部",
    "restaurant_name": "汁内实烧店",
    "meal_times_text": "晚餐",
    "pickup_method_text": "自提",
    "pickup_no": "004",
    "pickup_date": "2026-09-07",
    "pickup_time_range": "19:30-20:00",
    "pickup_address": "二层东侧取餐区",
    "locker_code": "",
    "locker_cell_no": "",
    "locker_status_text": "",
    "locker_store_time": "",
    "locker_pickup_time": "",
    "order_amount": "33.90",
    "discount_amount": "0.00",
    "pay_amount": "33.90"
  }
}

说明:手机号是否脱敏沿用当前后台权限政策;本需求不能自行改变。如果当前页面本来允许明文,新增 DTO 先保持现状,另行评审字段级脱敏。

8.2 查询策略

8.3 建议文件落点

文件 调整
Controller application/p/controller/MealOrder.php 路由与权限不变;只返回扩展后的详情
Logic application/p/logic/MealOrder.php 新增详情 DTO、枚举/时间/金额标准化、取餐号和档口查询
Model application/common/model/meal/Order.php 原关系兼容保留;如有必要扩充明确字段,不把业务映射塞入 Model
Frontend public/static/src/view/order/consume/index.vue 字段配置、动态三列、金额行、六菜品布局、错误态
Frontend test public/static/test/unit/specs/consumeOrderDetail.spec.js 字段优先级、显隐、六行、请求竞态、补打按钮兼容
Backend test tests/MealOrder/MealOrderDetailDisplayContractTest.php DTO、模式、枚举、空值、权限合同

9. 页面与交互设计

9.1 字段顺序

普通信息按下列顺序过滤空值后进入三列:

  1. 用户、手机号、订单状态。
  2. 订单编号、订单类型、订单来源、交易时间。
  3. 原部门、当前部门、档口、餐次。
  4. 取餐方式、取餐号、取餐日期、取餐时段、取餐地址。
  5. 取餐柜编号、格口、取餐状态、入柜时间、取餐柜取餐时间。

金额固定为底部三列:订单金额、优惠金额、实付金额;实付金额使用更高字重,不用新增主题色。

9.2 尺寸与滚动

9.3 与 VWCG-1078 的并行冲突

当前 develop_VWCG-1078_0908 已修改同一个 Vue 组件和 MealOrder Controller/Logic,并在详情 footer 增加补打按钮。VWCG-1334 应在 VWCG-1078 合入目标分支后再切分支,或在开发前语义合并,确保:

10. UC-MEAL-ORDER-EXPORT-002:按菜品明细导出消费订单

10.1 主流程

步骤 动作 / 系统响应 对象 AC
M1 管理员在消费订单列表按当前筛选提交导出 /p/mealOrder/export type=1 AC-E01
M2 Controller 将筛选条件与授权餐厅/部门范围写入 export_log DATA-EXPORT-01 AC-E02
M3 Cron 领取一条任务并重放权限 ExportMealOrder AC-E03
M4 Logic 以订单 ID 游标读取一批订单 ydy_meal_order AC-E04
M5 按本批 order IDs 一次查询菜品和取餐号并分组 initial_menu/meal_order_pickup AC-E05
M6 每条菜品写一行,重复订单字段;无菜品订单写一条空菜品行 XlsxStreamWriter AC-E06
M7 订单合计只累计一次,写合计行后原子完成文件 XLSX AC-E07
M8 导出日志显示成功并提供下载 export_log AC-E08

10.2 分支、异常与恢复

ID 条件 行为 恢复
A1@M6 一单多菜品 按明细稳定顺序逐行写;订单字段每行相同 正常结束
A2@M6 订单无菜品 保留一条订单行,菜品列为空,避免订单静默丢失 正常结束
A3@M6 非取餐柜订单 取餐柜列为空 正常结束
E1@M3 权限已变化 以执行时重放后的交集收窄,不扩大订单集合 重新提交合法范围
E2@M5 查询/写文件失败 abort() 清理临时文件,任务失败,不留下可下载的半文件 修复后重新提交
E3@M6 预计输出超过单 Sheet 上限 禁止截断;按确认方案拆 Sheet 或在提交前阻断并提示缩小范围 用户缩小筛选或启用多 Sheet
E4@M6 菜名为空/虚拟菜品 使用订单时可证明的名称或现有虚拟映射;仍为空则输出空,不输出 UUID 代替名称 数据治理另行处理

10.3 业务规则

11. XLSX 字段合同

建议表头:

分组 字段
订单基础 序号、订单编号、用户、手机号、订单状态、订单类型、订单来源、交易时间
组织与档口 原部门、当前部门、档口、餐次
取餐信息 取餐方式、取餐号、取餐日期、取餐时段、取餐地址、取餐柜编号、格口、取餐状态、入柜时间、取餐柜取餐时间
菜品明细 菜品名称、重量(克)、单价(元/单位重量)、数量(份)、消费日期、菜品金额(元)
金额信息 订单金额(元)、优惠金额(元)、实付金额(元)

说明:

12. 导出实现策略

建议继续修改 application/p/logic/MealOrder.php::exportOrdersToFile,不改 ExportMealOrder 的任务调度结构:

  1. 订单查询继续使用 meal_order.id 倒序游标,批次大小先沿用 1000。
  2. 每批收集 order_id,一次查询:
    • 菜品明细 + 菜品名称/图片所需字段;
    • 取餐号;
    • 必要的档口、设备与当前部门映射。
  3. 在批次内按 order_id 分组,不跨批次持有历史对象。
  4. 遍历订单时先累计一次订单合计,再遍历该订单菜品写行。
  5. 无菜品时写一条空菜品行,保证订单集合不丢失。
  6. finish() 成功后才暴露最终文件;异常执行 abort()

若新增普通自提/档口快照字段:

13. 权限、安全与隐私

14. 测试与验收矩阵

Test ID 层级 场景 通过条件
API-D01 后端契约 快照姓名/手机号存在 DTO 优先快照,不读取后改名值
API-D02 后端契约 快照为空 合法回退当前用户;均空则返回空字符串
API-D03 后端契约 堂食/自提/取餐柜/外部订单 枚举中文正确,不适用字段为空
API-D04 后端契约 取餐柜 0-4 状态、默认时间 状态/时间清洗正确
SEC-D01 权限 无菜单、无餐厅、无人员权限 详情拒绝且无字段泄漏
UI-D01 前端单测 空字段混排 不占空位,顺序稳定,金额 0.00 不隐藏
UI-D02 前端单测 快速连续查看两单 旧响应不能覆盖新订单
UI-D03 前端单测 VWCG-1078 合并后 补打按钮和 loading/disabled 行为不回归
VIS-D01 浏览器 1366×768、100% 6 行菜品直接可见,无表格内部纵向滚动
VIS-D02 浏览器 1920×1080、10+ 菜品 弹框整体滚动,表头/金额/footer 可辨与可操作
EXP-E01 后端单元 1 单 3 菜 输出 3 行,订单字段相同,菜品字段各自正确
EXP-E02 后端单元 订单无菜品 输出 1 条空菜品行,不丢订单
EXP-E03 后端单元 虚拟菜品/打包费 按确认口径输出,不出现空名称或内部 UUID
EXP-E04 后端单元 多订单多菜 排序稳定;订单合计不因拆行重复累计
EXP-E05 文件 生成 XLSX unzip -t 通过;表头、中文、列数和行数正确
SEC-E01 集成 筛选 + 餐厅/部门权限 导出订单 ID 集合与同条件列表一致
PERF-E01 性能 建议 20 万订单、平均 3 明细 约 60 万明细行,无 OOM、无持续内存增长;时限待确认
BOUND-E01 边界 预计超过 1,048,574 数据行 不截断;按已确认的多 Sheet/阻断策略处理

建议验证命令(BUILD 后执行):

vendor/bin/phpunit tests/MealOrder/MealOrderDetailDisplayContractTest.php
vendor/bin/phpunit tests/MealOrder/MealOrderDetailExportTest.php
vendor/bin/phpunit tests/DataPermission/ChangedInterfacesPermissionCoverageTest.php
vendor/bin/phpunit tests/Security/SecurityFixesTest.php
cd public/static && npm run unit -- consumeOrderDetail.spec.js
cd public/static && npm run build
unzip -t <generated-consume-order-file>.xlsx

还需在隔离测试环境按同一筛选调用列表与导出,比较唯一订单 ID 集合、订单数、菜品明细数和金额合计;只做文件能打开不算通过。

15. Definition of Ready 与待确认项

当前 DoR: BLOCKED。以下 P0 项关闭后才能进入开发:

ID P0 问题 推荐答案 影响 建议负责人
Q-001 普通自提地址和档口名称是否要求历史完全不随配置变化? 若“是”,新增订单快照字段并联动所有生产端;旧单只允许明确回退或留空 决定是否跨 store/ai_api、是否有 SQL 产品 + 后端
Q-002 「订单类型」和「订单来源」是否分别对应 order_type 在线/离线与 source 生产来源? 采用该映射,并给出历史非法值为空 决定详情/导出列语义 产品
Q-003 custom_dishpackage_fee 是否作为菜品明细行导出? 页面已有虚拟项;建议保留行,打包费同时增加订单级列时需另确认 决定金额对账和明细行数 产品 + 财务/运营
Q-004 展开后超过单 Sheet 上限如何处理? 优先按多个 Sheet 无损导出;若本期不做,提交前明确限制并提示缩小范围 决定 XLSX Writer 扩展和工期 产品 + 测试

P1 建议确认:

16. 开发切片与估时

以下为单人净开发估时,不含产品待确认、排队、真实大数据准备和上线窗口:

切片 内容 估时
S1 详情 DTO、取餐号/档口/状态/金额标准化、后端测试 1.5-2.5 人日
S2 Vue 动态字段、三列布局、六行与整体滚动、前端测试 1.5-2.5 人日
S3 异步导出逐菜品拆行、批量查询、去重合计、XLSX 测试 2.5-4 人日
S4 权限/列表-导出集合对比、四模式数据、构建与视觉验收 1.5-2.5 人日
合计 不新增快照字段 / 单 Sheet 受控策略 7-11 人日
可选增量 新增普通自提/档口快照,联动订单生产端和迁移 另加 3-6 人日
可选增量 XlsxStreamWriter 多 Sheet 支持和容量测试 另加 2-4 人日

进入 BUILD 后建议顺序:先锁定字段合同和测试数据,再完成详情端到端,随后改导出;不要先做 CSS 再补接口和快照。

17. 发布、兼容与回滚

18. 追踪矩阵

UC / 步骤 页面 API / 任务 数据 AC / Test 目标文件
DETAIL/M1-M2 消费订单/详情弹框 /p/mealOrder/detail orderId UI-D02 consume/index.vue
DETAIL/M3-M4 加载/失败态 详情权限与 Logic 订单、用户、档口、取餐号、柜机 API-D01-D04, SEC-D01 Controller/Logic/Model
DETAIL/M5-M6 三列/菜品表 详情响应 display/goods UI-D01-D03, VIS-D01-D02 consume/index.vue
EXPORT/M1-M3 列表/导出日志 /export + Cron export_log/query_conditions AC-E01-E03, SEC-E01 Controller/Cron
EXPORT/M4-M7 [不适用:后台任务] exportOrdersToFile 订单、菜品、取餐、XLSX EXP-E01-E05, PERF-E01, BOUND-E01 Logic/XlsxStreamWriter
EXPORT/M8 导出日志 /exportLogs 文件路径、状态 AC-E08 既有链路回归

19. 评审结论

20. 证据与人审