delivery.md 5.9 KB

029 商家自配送 — 交付说明

日期: 2026-09-08 | 分支: test-202609v2(按用户要求未单独开分支)

交付状态

部分 状态
后端(配置/判定/隔离/配送操作/结算/推送/i18n) ✅ 完成,43 个新增测试全绿;全仓 455/455 全绿零回归
数据库 DDL ✅ 已登记 updatesql/sql.md(2026-09-08 029 段,共 3 条)——需开发者手动执行后方可部署
商家端 PC(foodie-store) ⛔ 未做——见下「范围调整」
客户端 App / 商家手机 App 外部配合(清单见下)

上线前置清单(开发者手动执行)

  1. 在数据库执行 updatesql/sql.md 中「2026-09-08 029 商家自配送」的 3 条 DDL(pos_store 2 列、pos_store_self_delivery_hours 建表、pos_order 1 列)。未执行前部署新包会导致 pos_order 相关查询报列不存在。
  2. 执行后按 quickstart.md 场景 1-8 回归(服务启动 + 接口级冒烟)。
  3. (可选,T020 遗留)用有权限的账号跑下面的只读核验 SQL,确认夜市主门店行齐全;解析不到的夜市会自动回落骑手池(不报错,但那部分夜市自配送不生效):

    SELECT DISTINCT p.night_market_id
    FROM pos_store p
    WHERE p.is_stall=1 AND p.del_flag='0' AND p.night_market_id IS NOT NULL
    AND NOT EXISTS (SELECT 1 FROM pos_store m
                  WHERE m.is_night_market=1 AND m.del_flag='0' AND m.user_id=p.night_market_id);
    

范围调整:商家端界面落点(重要)

执行 T033 探索时发现:foodie-store(PC)没有门店信息页、也没有订单列表页(仅商品/分类/促销/对账等 17 个管理页);门店信息与订单管理界面在商家手机 App(uni-app,不在本工作区)。营业时段编辑也在 App 端。

因此 spec 中 FR-001(门店信息配置界面)与 FR-010(订单列表标识)的界面部分归入商家 App 外部配合;后端接口(/chanting/store/get|saveMdSelfDeliveryHours)与订单字段已就绪,App 端接入即可。是否在 PC 端新建门店设置/订单页面,与 clarify Q2(配送操作界面端覆盖)同属一个待决策问题,建议一并讨论后追加。

外部配合清单(客户端接入)

商家手机 App(uni-app):

  • 门店信息页:自配送区块——开关 + 模式(全天/自定义按星期)+ 星期×多时段编辑;调 POST /chanting/store/saveMdSelfDeliveryHours(DTO: mdId/enabled/mode/hours[{dayOfWeek,startTime,endTime}]),读 GET /chanting/store/getMdSelfDeliveryHours(返回 enabled/mode/hours/configMdId)。摊位主(isStall=1)保存会被拒(no.store.stall.selfdelivery.readonly),前端按 configMdId≠自身做只读展示"由夜市主统一配送"
  • 订单列表/详情:selfDelivery==1 显示"自配送"标签,不显示等待骑手接单状态
  • 配送操作(开始配送/确认送达,可与 Q2 决策合并):POST /system/orderShOprate/selfDeliveryStart / selfDeliveryComplete(DTO: {id, qsImg?});开始配送为可选步骤

客户 App(uni-app):

  • 订单对象已自动携带 selfDelivery 字段(0/1);selfDelivery==1 显示"商家自配送",配送状态映射:deliveryStatus 0=备餐中、2=商家配送中、3=已送达(无骑手接单环节)
  • 推送文案 key:no.message.push.merchant.delivery.start / no.message.push.merchant.delivery.complete(5 个 properties 已含四语言)

实现要点(代码导航)

  • 判定:SelfDeliveryUtil.matches([开始,结束)、ISO 星期)+ SelfDeliveryService.resolveJudgeTime(即时=下单时刻;预约按 delryTime 时间段开始时刻,格式 yyyy-MM-dd HH:mm-HH:mm,解析失败回落即时)
  • 快照:两个下单入口(UserOrderController/PosOrderShOprateController 的 createOrderChild)写 pos_order.self_delivery,终身不变
  • 隔离:骑手 newTask 池过滤 + accept/pickup/deliver 直调拒绝(no.order.selfdelivery.rider.denied)
  • 放行:requireRiderAssigned 对 self_delivery=1 跳过骑手校验
  • 操作:selfDeliveryStart(state=2 且 deliveryStatus=0→2)/ selfDeliveryComplete(state=2 且 deliveryStatus∈{0,2}→原子置 3+state=3+sdTime),Redisson 锁 + CAS
  • 结算:OrderService.setSelfDeliveryFreightBilling(UserBilling type="5"、user_id=sh_id、全额、divvy=0);挂在商家送达与 OrderLifecycleService.completeSideEffects(管理端路径)两处;到付 type="3" 账单 paymentId=sh_id
  • 快照不回滚:判定只在创建时发生,配置后续变更不影响已创建订单(结构性保证)

核验与修复记录(2026-09-08 子代理核验)

核验结论:FR-001~FR-012 逐条落实(FR-007/010 界面部分归客户端仓库);发现并已修复:

  1. 【中】确认送达未校验支付状态——商家POS现金/转账外送单(payStatus=0)曾可被直接送达完成并生成分成账单;已加 payStatus==1 前置(no.order.selfdelivery.unpaid),对齐骑手送达语义
  2. 【中】FR-009"商家接单"节点推送缺失——自配送单已补:商家接单后推用户(no.message.push.merchant.accepted.content)
  3. 【低】时段重叠校验、pickup/deliver 直调拒绝测试、两处过期注释、既有缺口 key no.order.not.found(5 个 properties 补齐)

已知隐患(本期不改,记录备查):平台管理端 GET /system/order/list 走 XML 显式列清单不含 self_delivery 列——管理端界面按 D10 不在本期范围,未来管理端需要展示自配送标识时须同步 PosOrderMapper.xml 的 selectPosOrderVo。

修复后全量回归:458/458 全绿。

遗留决策

  1. clarify Q2(配送操作界面端覆盖)——用户暂缓;后端接口先行,PC/App 界面待决策
  2. PC 端是否新建门店设置/订单页——随 Q2 一并讨论
  3. T040 运行时冒烟待 DDL 执行后进行(quickstart 场景 1-8)