delivery.md 8.7 KB

029 商家自配送 — 交付说明

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

交付状态

部分 状态
后端(配置/判定/隔离/配送操作/结算/推送/i18n) ✅ 完成,43 个新增测试全绿;全仓 455/455 全绿零回归
数据库 DDL ✅ 已登记 updatesql/sql.md(2026-09-16 补登记 029 段,共 3 条)——需开发者手动执行后方可部署
商家端 PC(foodie-store) ⚠️ 夜市主自配送设置页已完成;无订单列表/详情及配送操作入口
客户端 App(msd-user) ⛔ 按本次范围不修改,待外部接入自配送展示与骑手入口隔离
商家手机 App ⛔ 源码未在当前工作区,仍需外部接入配送操作

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

  1. 在数据库执行 updatesql/sql.md 中「2026-09-16 补登记商家自配送表结构(029-merchant-self-delivery)」的 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)没有既有的门店信息页和订单列表页(仅商品/分类/促销/对账等管理页);因此已额外落地一个仅供夜市主使用的“自配送设置”页,但订单管理界面仍在商家手机 App(uni-app,不在本工作区)。

因此 FR-001 的夜市主配置子集已由 PC “自配送设置”页承接;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,msd-user):

  • 按本次范围不修改;后续如需接入,订单对象使用 selfDelivery 字段(0/1):列表与详情显示"商家自配送";deliveryStatus 0/1=商家备餐中、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。

补正记录(2026-09-16)

  1. 029 的 3 条 DDL 曾缺失于 updatesql/sql.md,现已补登记;仍由开发者手动执行,应用不会自动迁移数据库。
  2. 已支付自配送外送单的创建、LINE Pay/OMG 支付成功及线下转账确认均复用 DeliveryOrderNotificationService.notifyOrderAvailable。该公共入口现统一分流:自配送订单仅通知门店/夜市主,其他外送订单才通知骑手;新增回归测试覆盖,避免骑手收到无效新单推送。
  3. user_billing.type="5" 固定表示自配送运费收入;已停止的“金额解冻”业务不再占用该类型。商家端筛选与列表显示统一为“自配送运费”(四语言),骑手和商家商品分成仍沿用 type="0"。
  4. 用户下单请求未传 shId 时,服务端以实际 mdId 对应门店的 userId 回填;已传 shId 保持旧客户端兼容行为。夜市摊位门店的 userId 为夜市主,因此自配送运费归属保持正确。
  5. 用户端 msd-user 按本次范围不修改,仍待接入自配送标识、状态文案与骑手入口隔离;商家手机 App 的订单页面源码不在当前工作区,仍须接入 /system/orderShOprate/selfDeliveryStart|selfDeliveryComplete。

历史全量回归记录保留于原始交付;本次补正仅执行定向后端测试、商家端生产构建和用户端语言 JSON 解析,详见本次提交说明。

遗留决策

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

已记录待实施方案:夜市主 PC 订单配送调度(2026-09-16)

确认不新建页面,复用 foodie-store 首页的“最新订单”管理区。夜市主(userType=3)不负责摊位接单和出餐,只管理旗下摊位已出餐的自配送外送单;摊位主继续使用既有“接单 → 出餐”操作。

夜市主仅展示 type=0 && selfDelivery=1 的订单,状态 Tab 为:

  1. 全部:只读查看自配送订单详情。
  2. 备餐中(state=0/1):只读,等待摊位主接单、出餐。
  3. 待配送(state=2 且 deliveryStatus=0):显示“开始配送”,并可直接“确认送达”。
  4. 配送中(state=2 且 deliveryStatus=2):显示“确认送达”。
  5. 已完成(state=3 或 deliveryStatus=3):只读。
  6. 已取消/售后:只读。

订单行与详情需标明“自配送”和摊位名称;“开始配送”“确认送达”分别调用既有 selfDeliveryStart、selfDeliveryComplete。前端仅控制显示,接口仍以服务端订单归属、selfDelivery、订单状态和配送状态重新校验。