需求基线:
需求说明书.htmlv1.0(2026-07-26,状态:需求确认中) 代码基线:master @ 2026-09-03 实地核对(非文档推断) 配套文档:酒店模块迁移跟踪.md— 代码地图与实现进度(回答「代码里有什么」) 本文档职责:需求视角的验收对照与缺口台账(回答「需求要什么、还差什么」) 条目归宿:缺口关闭后不在本文档堆积实现细节,转入code-copilot/changes/<变更名>/,本文档只留状态与链接
需求定义五端协同(顾客端 / 餐厅前台 / 厨房端 / 宾馆前台 / 后台管理),当前实际落地为「1 个顾客端 H5 + 1 个 PC 管理端(兼餐厅前台与宾馆前台职能)」。
| 判断 | 结论 |
|---|---|
| 主链路 | ✅ 扫码 → 房号确认 → 点餐 → 购物车 → 下单 → 支付 → 接单/备餐/出餐/配送/完成 → 退单退款,已跑通 |
| 厨房端 | ❌ 完全缺失(0 页面、0 打印、0 菜品语音播报) |
| 微信支付 | ❌ 缺失,微信环境实际落到 Mock 分支 |
| 小程序载体 | ❌ 缺失,manifest.json 中 mp-weixin.appid 为空、mp-alipay 无 appid,实际以 H5 运行 |
| 品牌风格设置 | ❌ 缺失(三套配色 / 五端同步),对应 hotelconfig 模块整体暂缓 |
| 订单金额正确性 | 🔴 存在资金缺陷:规格加价与加料加价在后端被丢弃,详见 3.1 |
| 开放接口越权 | 🔴 存在数据隔离缺陷:tenantId 由前端传参、订单详情不校验会话归属,详见 3.2 |
| 超出需求的增量 | ➕ 在住会话 / 房态看板 / 退房联动 / 退款全链路 / 支付超时取消,详见第七节 |
| 端 | 需求要求 | 落地情况 | 载体 |
|---|---|---|---|
| 📱 顾客端 | 微信/支付宝小程序,8 页 | 8 页全部实现,但运行在 H5,小程序未发布 | forge-h5-ui/src/pages/hotel/customer/ |
| 🖥️ 餐厅前台 | PAD / 移动 / PC,4 页 | 无独立端,职能由 PC 订单页承担;缺「临时暂停接单」「移动端」 | views/hotel/order.vue、views/hotel/businessHours.vue |
| 👨🍳 厨房端 | 大屏 + 打印机,2 页 | 完全未实现(仅「出餐」动作可在 PC 订单页点击) | — |
| 🏨 宾馆前台 | PC / 移动,2 页 | 无独立端,PC 订单页覆盖全订单查看/筛选/任意阶段退单/手动退款;缺移动端 | views/hotel/order.vue |
| ⚙️ 后台管理 | PC,5 页 | 菜品/订单/房间三页齐备且更强;数据看板指标不全、系统设置缺失 | views/hotel/ |
AGENTS.md 5.9 安全红线:涉及资金,必须在 Spec 中标注并经人工审查。
证据链
| 环节 | 位置 | 行为 |
|---|---|---|
| 前端算价 | forge-h5-ui/src/pages/hotel/customer/dish-detail.vue L317-319 |
unitPrice = 菜品基础价 + Σ priceExtra(规格) + Σ extraPrice(加料) |
| 前端提交 | forge-h5-ui/src/pages/hotel/customer/order-confirm.vue L141-148 |
items 只带 specDesc / additionsDesc 文本,不带规格选项 ID / 加料 ID |
| 后端重算 | HotelOrderServiceImpl.orderCreate() L158-175 |
unitPrice = dish.getPrice() 仅基础价,覆盖前端传值;subtotal = 基础价 × quantity |
| 表结构 | HotelOrderItem / hotel_order_item |
只有 specDesc、additionsDesc 冗余文本,无 spec_option_ids / addition_ids → 后端即使想重算也缺少关联依据 |
后果(当前即可复现)
pay.vue 的金额取自后端 orderDetail,因此支付页金额 < 下单页/购物车金额,用户可见。修复方向
V1.0.108:hotel_order_item 增加 spec_option_ids、addition_ids(或 JSON 价格快照列)OrderCreateDTO.items 增加规格选项 ID 列表 + 加料 ID 列表orderCreate() 按 ID 从 hotel_dish_spec_option.price_extra + hotel_dish_addition.extra_price 服务端重算 unitPrice,继续不信任前端价格specDesc / additionsDesc 保留为冗余展示)需求 7.安全要求:「订单数据隔离,各端仅可见本端权限范围内数据」。
证据链
SaTokenConfig L79 / L104:/hotel/open/** 同时排除登录校验与 API 权限校验HotelCustomerController 全部 8 个接口的 tenantId 由请求参数传入,服务端无凭证校验HotelOpenController#scan 返回的 tenantId 无签名、无时效,可被任意复用HotelOrderServiceImpl.requireOrder() L478-487:仅校验 tenantId 一致,不校验订单是否属于当前 stayIdorderPage 的 stayId 过滤条件由前端传入,不是安全边界后果
tenantId 可跨租户读取菜品/订单。orderId 即可查看任意订单详情(含房号、联系人、联系电话)—— 违反需求「接单前顾客自助退」的权限边界。deliveryFee 由前端传值(OrderCreateDTO.deliveryFee → orderCreate() L149 直接落库、L174 计入总额),免登录接口可构造任意值(含负数)。当前 H5 hotel-order.js L17 的 hotelConfig.deliveryFee 无数据来源恒为 0,暂无实际资金损失,但接口层可篡改。修复方向
tenantId + roomId + stayId + 过期时间),后续 open 接口从 token 解析上下文,取消前端传 tenantIdorderDetail / orderPage / customerRefund / abandonOrder 强制按 token 中的 stayId 校验归属deliveryFee 改为服务端配置读取(依赖 hotelconfig 模块),过渡期至少做 >= 0 与上限校验| 项 | 现状 |
|---|---|
| 前端 | pay.vue L126-136:条件编译 + UA 检测(micromessenger)→ paySource = 'WECHAT' |
| 后端 | HotelPayServiceImpl.createPay() L113:if ("ALIPAY".equals(paySource)) … else 全部落到 Mock 分支(L148-149) |
| 前端兜底 | pay.vue L258-276:非 ALIPAY 分支直接调 hotelPayMockSuccess → 显示「支付成功」但没有真实扣款 |
| 退款 | refundOrder() L467-473:MOCK 渠道直接标记退款成功,无真实资金流 |
修复方向:补 WECHAT 分支(微信内 H5 用 JSAPI 支付,需 openid;扫码公众号/小程序另议)+ 微信回调验签 + 微信退款接口。
前置依赖:微信商户号资质(当前无公司资质,见第八节待确认项)。
| 需求项 | 优先级 | 状态 |
|---|---|---|
| 待做订单列表(按接单时间排序) | P0 | ❌ 无页面 |
| 语音播报菜品内容 | P0 | ❌ 仅固定 MP3 提示音,无 TTS 播报菜品名 + 房号 |
| 自动打印小票(菜品/房号/备注) | P0 | ❌ 全仓无任何打印相关代码 |
| 出餐操作 | P0 | ✅ 借道 PC order.vue → /hotel/order/ready |
修复方向:新建厨房大屏页面(PC 端或 H5 横屏),SSE 订阅接单事件;小票可先做浏览器 window.print() 模板,热敏打印机(需求要求 1920×1080 大屏 + 打印机)后置。
SseEmitterManager 当前只有 NEW_ORDER、REFUND_REQUEST 两类事件orderAccept / orderReady 均无任何通知发布修复方向:SseEmitterManager 增加 ORDER_ACCEPTED、ORDER_READY 事件;orderAccept() / orderReady() 发布;PC useOrderNotification.js 增加对应提示;厨房端订阅。
forge-h5-ui/src/manifest.json:mp-weixin.appid = "",mp-alipay 无 appid 配置AlipayAuthController#getUserInfo(支付宝授权取姓名 + 手机号)、pay.vue 中 #ifdef MP-ALIPAY 的 my.tradePay 分支已知外部约束(历史结论,需复核):支付宝沙箱 App 扫码不识别普通 URL 二维码;小程序 web-view 需已备案业务域名,临时隧道域名不满足。
修复方向:注册小程序账号 → 配置 appid → 原生页面或 web-view 嵌入 H5 → 购买并备案域名 → 接入真实支付。
| 需求项 | 优先级 | 状态 | 实现位置与说明 |
|---|---|---|---|
| 扫码进入 | P0 | ✅ | 短码机制(URL 带 c 参数)+ GET /hotel/open/scan;一房一码、批量生成/绑定/解绑/ZIP 下载 |
| 房号确认 | P0 | ✅ | room-confirm.vue,额外做了「非入住房间拦截点餐」 |
| 菜单浏览 | P0 | ✅ | menu.vue:分类横滚、搜索、售罄标识、购物车浮层 |
| 菜品详情 | P0 | ✅ | dish-detail.vue:大图轮播/规格(必选/可选)/加料/备注/数量(超出需求) |
| 购物车 | P0 | ✅ | cart.vue + Pinia 持久化,specKey 合并同规格 |
| 确认下单 | P0 | ✅ | order-confirm.vue:房号/联系人/立即或预约送达/备注 |
| 在线支付 | P0 | ⚠️ | 支付宝沙箱 H5 WAP Pay 全链路(payForm 自动提交 + 轮询 + 主动查询兜底 + 5 层安全校验);微信支付未实现(见 3.3) |
| 订单状态追踪 | P0 | ✅ | order-status.vue:进度条 + 10 秒轮询;状态 0~11,比需求 6 节点更细 |
| 退单(接单前) | P0 | ✅ | customerRefund:待支付直接取消、待接单直接退 + 自动全额退款 |
| 我的订单 | P1 | ✅ | orders.vue:分页、状态标签、待支付「去支付」 |
| 营业时间提示 | P1 | ✅ | 后端新增 BusinessHoursStatusVO + currentStatus() 推导 + GET /hotel/open/customer/businessHours 免登录接口;H5 三页接入:room-confirm.vue 入口整页拦截、menu.vue 菜品区独立校验(防 TabBar 绕过)、order-confirm.vue 提交前刷新阻断,后端 orderCreate() 兜底校验。变更目录:code-copilot/changes/hotel-customer-business-hours-check/ |
| 需求项 | 优先级 | 状态 | 说明 |
|---|---|---|---|
| 新订单语音提醒 | P0 | ✅ | SSE + MP3(女生-订单提醒.mp3)+ 桌面通知 + 3 秒防抖合并 + Chrome 自动播放策略绕过 |
| 接单/拒单 | P0 | ✅ | /hotel/order/accept、/reject(拒单必填原因,且自动退款) |
| 订单处理流转 | P0 | ✅ | accept → prepare → ready → deliver → complete |
| 营业时间设置 | P0 | ✅ | businessHours.vue:多时段、按星期、启禁用 |
| 出餐通知接收 | P0 | ⚠️ | 见 3.5,无出餐通知事件 |
| 当日订单看板 | P1 | ⚠️ | order.vue 工具栏 5 指标(待接单/备餐中/配送中/今日订单/今日营收);缺「预计送达时间」一览 |
| 临时暂停接单 | P1 | ❌ | 无字段、无接口、无开关 |
需求要求语音提醒「可暂停/调整音量」,实际
useOrderNotification.jsL144audio.volume = 0.8硬编码,无任何控制 UI。
见 3.4。
| 需求项 | 优先级 | 状态 | 说明 |
|---|---|---|---|
| 全订单查看 | P0 | ✅ | /hotel/order/page,不限状态 |
| 订单状态总览 | P0 | ✅ | 房间号/状态/联系人/支付方式筛选 |
| 退单(任何阶段) | P0 | ✅ 更强 | hotelRefund(任意阶段强制退)+ refundApprove/refundReject(审核)+ 自动全额退款 + 手动退款重试(refund_status=2) |
| 订单详情查看 | P1 | ⚠️ | 详情弹窗有基本信息/金额/时间记录/菜品明细,无可视化「状态时间线」 |
| 需求项 | 优先级 | 状态 | 说明 |
|---|---|---|---|
| 菜品管理 | P0 | ✅ | 上下架/售罄恢复/价格/主厨推荐/多图预览/分类启禁用/规格组 + 选项(按菜品挂载);⚠️ PC 无加料编辑入口(见 5.10) |
| 订单管理 | P0 | ✅ | order.vue |
| 房间管理 | P0 | ⚠️ | 房间/房型 CRUD + 二维码批量生成绑定下载 + 房态看板(board.vue,V1.0.107 建菜单);缺「二维码重生成/补打」独立动作、缺「启停点餐」字段(HotelRoom 无该字段) |
| 数据看板 | P1 | ⚠️ | 今日订单数、营业额 ✅;热门菜品 ❌、平均送达时长 ❌、趋势图 ❌。后端 GET /hotel/dish/ranking(dishSalesRanking)与前端 getDishSalesRanking() 均已就绪但无任何页面调用 |
| 系统设置(品牌风格) | P1 | ❌ | hotelconfig 模块整体暂缓;H5 主题硬编码 styles/hotel-theme.css(墨绿 #2D5016),三套配色与五端同步都没有 |
| # | 缺口 | 落点 | 备注 |
|---|---|---|---|
| 5.1 | ✅ 已完成。后端 BusinessHoursStatusVO + currentStatus() + 开放接口 + 下单校验;H5 三页接入(入口拦截 / 菜品区拦截 / 提交阻断)。变更目录:code-copilot/changes/hotel-customer-business-hours-check/ |
||
| 5.2 | 数据看板补齐(热门菜品/平均送达时长/趋势图) | OrderDashboardVO 扩字段 + 新增看板页 |
ranking 接口可直接复用 |
| 5.3 | 系统设置 / 品牌风格配色(三套方案、五端同步) | 重启 hotelconfig 模块(跟踪文档 3.1) |
含待讨论的配送费 / 配送时长 |
| 5.4 | 临时暂停接单 | hotel_business_hours 或酒店配置加开关 + 下单前校验 |
需 Flyway 新增开关字段。与 5.1 不合并(人为暂停 vs 时段外语义独立,合并会使 fail-open 判定复杂化),但可复用 5.1 建好的提示条与阻断链路 |
| 5.5 | 语音提醒可控(音量/暂停)+ TTS 播报菜品名与房号 | useOrderNotification.js |
需求 7.语音要求 |
| 5.6 | 当日看板显示预计送达时间 | order.vue 看板区 |
数据已有 customDeliveryTime |
| 5.7 | 房间「启停点餐」开关 + 二维码重生成/补打 | HotelRoom 加字段、qrcode.vue 加动作 |
需 Flyway |
| 5.8 | 订单状态时间线可视化 | order.vue 详情弹窗 |
时间字段齐备 |
| 5.9 | 餐厅前台 / 宾馆前台移动端(PAD) | H5 工程新增管理侧页面 | 需求 2.角色架构 |
| 5.10 | PC 端菜品加料编辑入口 | views/hotel/dish.vue 表单 |
后端 HotelDishDTO.additions + saveAdditions() 已支持,H5 已在读 dish.additions,等于数据无入口维护 |
| 5.11 | 菜品操作日志 | HotelDishServiceImpl 各操作埋点 + 查询接口 |
hotel_dish_log 表与 HotelDishLogMapper 已建,零业务代码(无写入、无查询) |
| 5.12 | 顾客端状态更新延迟 | 轮询改推送或缩短间隔 | 需求要求 < 3 秒,现状 10 秒轮询不达标 |
| 类别 | 需求 | 现状 |
|---|---|---|
| 性能 | 扫码进入 < 2s、菜单加载 < 1.5s、状态更新 < 3s | ⚠️ 未做专项压测;状态更新为 10 秒轮询,不达标(PC 端 SSE 实时达标) |
| 兼容 | 微信/支付宝小程序 | ❌ 未落地(H5 替代) |
| 兼容 | Chrome 90+ / Safari 14+ / 移动端浏览器 | ✅ PC 管理端 + H5 满足 |
| 兼容 | 厨房大屏 1920×1080 + 热敏打印机 | ❌ 无厨房端、无打印 |
| 语音 | 前台新订单语音提醒(可暂停/调音量) | ⚠️ 有提醒,无音量/暂停控制 |
| 语音 | 厨房播报菜品名称和房号 | ❌ 未实现 |
| 视觉 | 简洁商务风、留白多、扁平化 | ✅ H5 亚朵墨绿主题符合 |
| 视觉 | 三套候选配色(商务深蓝/暖金雅致/墨绿清新)待客户确认 | ❌ 仅墨绿一套且硬编码 |
| 视觉 | 品牌风格设置后五端同步生效 | ❌ 未实现 |
| 视觉 | 移动端底部 Tab 导航 / PC 左导航右内容 | ✅ HotelTabBar 3-tab + Forge 后台布局 |
| 安全 | 支付环境自动判断,防支付方式注入 | ⚠️ 判断在前端(UA + 条件编译),paySource 由前端传,非服务端环境判断 |
| 安全 | 退单权限控制(接单前顾客自退、接单后仅宾馆前台) | ⚠️ 后端状态机规则正确,但开放接口无身份凭证,权限边界可被绕过(见 3.2) |
| 安全 | 订单数据隔离 | ⚠️ 租户 + stayId 双重隔离已设计,但 stayId 由前端传参,非安全边界(见 3.2) |
需求文档 v1.0 未包含、但已实现并构成产品竞争力的部分:
hotel_room_stay:入住/退房/打扫完成/维护切换 + 退房联动取消待支付订单(状态 11)+ 退房二次确认(needConfirm + 进行中订单清单)+ 订单挂 stay_id + 退房后购物车自动清空views/hotel/board.vue(V1.0.107 建菜单):房间卡片网格 + 状态 Tab + 楼层/房型筛选 + 入住登记/退房/打扫/维护/入住记录PayTimeoutTask:@Scheduled(fixedDelay=120000) 每 2 分钟跨租户扫描 selectExpiredUnpaidOrderIds → cancelTimeoutOrder(状态 10)abandonOrder:物理删除订单与明细(未支付无审计留痕要求),H5 侧恢复购物车AlipayTradeRefundRequest + refund_status 0/1/2 + 4 个自动触发点(拒单/顾客退单/前台退单/审核通过)+ 手动重试 + 回调竞态防护(已取消订单 7/9/10/11 收到回调不复活,流水标记异常)/hotel/open/alipay-auth/getUserInfo(authCode → 姓名,加密响应 → 手机号),为小程序化预留/hotel/notification/remote/notifyNewOrder,解决双 JVM 内存不共享;server.type 配置区分服务角色| # | 待确认 | 影响的缺口 |
|---|---|---|
| 1 | 顾客端载体:真小程序(需资质 + 备案域名)还是继续 H5? | 3.3、3.6 |
| 2 | 微信支付资质:是否有商户号?无则微信内扫码如何收款 | 3.3 |
| 3 | 厨房端硬件:大屏规格、热敏打印机型号与对接方式(驱动/ESC-POS/云打印) | 3.4 |
| 4 | 三套配色方案选哪套,是否真需要「五端同步生效」 | 5.3 |
| 5 | 配送费规则:按距离/按区域/固定?是否阶梯定价 | 3.2、5.3 |
| 6 | 预计配送时长:固定值还是动态计算 | 5.3、5.6 |
| 7 | 临时暂停接单粒度:全店暂停还是按餐段暂停 | 5.4 |
| 8 | 规格/加料加价是否为正式业务规则(决定 3.1 修复优先级) | 3.1 |
第 0 批(资金与安全,最高优先,不等需求确认)
3.1 订单金额计入规格/加料加价 —— 少收款 + 支付页金额不一致,属线上缺陷3.2 开放接口签名 token + 订单归属校验 + deliveryFee 服务端化第 1 批(打通真实收款 + 厨房闭环)
3.3 微信支付分支 → 4. 3.4 厨房端待备餐/出餐页 → 5. 3.5 SSE 接单/出餐事件 → 6. 小票打印(先浏览器打印模板)第 2 批(顾客端合规上线)
5.1 营业时间校验接入 H53.6 小程序 appid + 备案域名 + web-view → 9. 5.12 状态推送满足 < 3s第 3 批(运营与管理完善)
5.2 数据看板补齐 → 11. 5.3 hotelconfig + 品牌配色 → 12. 5.4 临时暂停接单 → 13. 5.10 加料 PC 入口 + 5.11 操作日志埋点 → 14. 5.7 房间启停点餐 + 二维码补打 → 15. 5.9 前台移动端每一项落地时按 AGENTS.md 2.1 走
/propose <需求>→code-copilot/changes/<变更名>/spec.md + tasks.md,本文档只更新状态列。
| 日期 | 变更 | 说明 |
|---|---|---|
| 2026-09-03 | 建档 | 基于 需求说明书.html v1.0 与 master 代码实地核对生成;同步修订 酒店模块迁移跟踪.md 中与代码不一致的 7 处 |
| 2026-09-03 | 5.1 出提案 | 新增变更目录 code-copilot/changes/hotel-customer-business-hours-check/(spec.md + tasks.md);补记 room-confirm.vue L107 营业时间硬编码且从未赋值的新发现;修正 5.4 与 5.1 的关系为「不合并」 |
| 2026-09-03 | 5.1 ✅ 已关闭 | 后端新增 BusinessHoursStatusVO + currentStatus() + 开放接口 + 下单校验;H5 三页接入(入口整页拦截 / 菜品区独立校验 / 提交阻断);后端编译通过 + H5 构建通过 + 硬编码零残留 |