Branch: 当前 codex/036-multi-store-checkout,从 test-202609v2 创建;平台管理 PC 端仓库使用同名分支。
Date: 2026-10-12 | Spec: spec.md
阶段: 后端接口、持久状态与恢复任务已编码并通过本地回归;036 SQL 仅登记未执行,真实数据库/渠道验证、App 联调和平台退款跟进 PC 页面仍待验收,不开放配置开关。
2026-10-20 平台最小运营闭环:沿用 GET /system/order/list 的子单分页,服务端按当前页子单 ID 批量补真实 036 成员关联与批次/组状态,普通单保持原行;详情新增 system:order:list 权限的后台批次只读接口,复用成员及原渠道退款投影,不复用带用户身份校验的 App 详情。平台子单详情增加批次入口,批次详情只读展示全部成员和双进度。已有逐笔退款跟进只读接口接 PC 页,四语言补齐;不能提供手工置已退款入口。平台普通状态修改对 036 成员在前端隐藏、后端直接调用也拒绝,普通单操作不变。两仓代码已实现并经本地测试/构建,真实数据库、支付渠道和 App 联调仍待完成;两个仓库均沿用既有功能分支,不新建分支。
2026-10-16 用户要求用户端、骑手端的多店列表一起规划,并参考闪送多点列表。2026-10-19 已将 两端列表与详情联合方案 收口到 API 契约 和 App 接入说明:一批/一组一张卡、一个主流程工作页签、取送双进度、服务端聚合后分页及角标;已撤回同组跨两个工作页签同时展示的旧建议。用户确认待抢距离按首店判断、旧版两端 App 升级后才能使用;对应后端只读接口及能力头已编码,App 页面与联调未完成。
2026-10-16 用户已确认项目审查的四项建议,见本文末「项目审查」和 spec.md FR-042~045;纳入本轮实施与验收。其中 037 已实现独立订单无人接单取消,本轮接入 036;此前“无人接单取消后续处理”的说明由本次决定覆盖。业务代码尚未因此完成。
保留“一次结算父单、每店独立子单、各店独立收款”。父单管本次确认参与集合的准入、生效、超时和取消;配送组管同骑手履约,不能把整份购物车等同于本次结算。用户已澄清:同骑手A/B父批次不包含圈外C,也不等待C付款;普通不合并结算不创建组。
付款后不改收款归属、不跨商家调拨。取消时按原支付交易使用对应商家凭证退款。OMG 正式退款已按用户最新要求纳入;环境限制是验证限制,不是砍掉功能的理由。
用户已确认首版纳入即时平台外送、预约平台外送、到付;夜市自配送、自取沿用原流程。预约支持在线付款或到付。同一用户、同一地址仍是合并前提;新多店批次不支持线下转账、原单店转账不变已确认(Q13)。堂食暂不纳入仍为方案建议。
2026-10-12用户决定预约时间处理改造后续单独进行;2026-10-16 补充确认本轮接入 037 既有无人接单超时规则,并检查尚未生效预约是否已过期。不撤销预约多店支持,不重定义原预约语义或新增倒推最迟接单算法,详见3.1及 FR-042~043;不能把确认当作旧链路或新代码已验证。
用户指出各商家可独立设置支付方式,撤回“同批统一付款方式/渠道”的建议。改为按每家店提供可用方式、用户逐店选择、子单独立保存payType;父批次只汇总,不覆盖子单。LINE/OMG/到付混用及统一生效已确认;新批次过滤转账,不改原单店流程(Q13)。预约时间改造延期见Q12。
2026-10-21 当前口径:建批时逐店明确选择方式,到付当场确认、线上待真实付款;全到付可在建批时进入统一激活流程。所有新批次仍按创建时间快照15分钟线上发起/合法改选截止及默认5分钟可配置的最终确认缓冲;第15分钟停止新发起、重试和改选,截止前已实际发起的线上尝试可在缓冲内完成付款与核实。最后成员合法就绪即生效,但ACTIVE不代表组已开放;即时/已到预约时点在事务提交后立即尝试开放,未到时点及失败重试由分钟任务处理。旧非多店规则不变。
以用户选择的第一家店A为圆心,默认范围1公里、在平台合并配置中可调整;后续店分别与A按坐标直线距离比较,不按道路路线距离,不从B继续扩圈、不以收货地址为圆心。2026-10-13用户明确确认直线距离及范围可配置,不把1公里写死在代码。配置必须有效且大于0,缺失/非法时保守关闭合并;具体单位/精度在data-model/contracts明确,前后端一致。已确认A/B圈内、C圈外时,C不参与本次合并父批次/配送组,A/B的付款生效、金额和折扣不等待或包含C。撤回原Q2“C同批独立配送且ABC一起付款”建议;也不采用“购物车有C就禁止A/B合并”的上一条解释。
接入建议:预结算返回本次可合并参与店和圈外排除原因,用户确认本次A/B后提交;C保留购物车供另行结算,不自动建C单/收费、不静默丢弃商品。最终创建仅写A/B父子单、组和快照;若请求夹带C或成员与确认报价不同,应明确拒绝并重报,不在事务内偷偷过滤。圈内参与不足2店不形成合并组;删除首店/改变成员或地址需重算及确认。
已有C另行结算订单的状态、付款、取消或退款也不影响A/B父批次。统一生效检查只查询该父单真实成员,不扫描用户所有订单或购物车全部店。此边界与“用户是否选同骑手”分开;不选同骑手时各店按原运费计费的规则不因本次澄清改变。
复用StorePayMethodController和PaymentMethodGateService逐店查询平台/商家/凭证可用方式,不取跨店交集。2026-10-21 调整:建批请求必须逐店提交paymentMethods:[{storeId,payType}],服务端与参与店及最新报价的可用方式一一核对,固化子单和成员支付方式;线上只进入ONLINE_PENDING,到付进入COD_CONFIRMED,全到付建批即可激活。幂等摘要包含逐店选项。批次待生效、操作截止前且该店没有任何网关尝试时,专用支付入口允许安全改选;一旦请求已发往网关则先核实原交易,不能换渠道。旧OrderCreateInput全局paymentMethod及旧单店行为保留。
三店均参与、A选LINE/B选OMG/C选到付:建批时C即为COD_CONFIRMED,A/B须在15分钟期限内由服务端记录并实际发起支付尝试,可在默认5分钟可配置缓冲内完成真实付款和核实。最后成员就绪才生效,部分就绪不提前推送或被抢;全到付建批即可激活,但实际可抢仍取决于开放时点与opened_at。缓冲不允许新发起、重试或改选;两个截止在创建时固定,不因逐店付款/重试延长。
2026-10-21 开放执行补充:线上最后付款、到付最后确认和全到付建批在同一事务内只写 ACTIVE 与持久开放事件;提交后通过独立事务按批次取事件,调用与分钟任务共用的开放逻辑。即时单和已到旧预约开放时点的批次立即开放;未到时点保留 PENDING,分钟任务处理到点及失败/重启补偿。开放事务只写子单显示、组 opened_at 和事件 OPENED;通知仍由独立消费者处理。新版骑手列表/角标方法归 PosOrderQsOprateController,对外 URL 和聚合协议保持不变,旧列表只供兼容。
取消/超时按每笔真实收款处理:A/B 已实收则原渠道退款,C 未实收只取消、不产生网关退款。支付进度分别展示线上已付/待付和到付约定,不能把到付计为已实收。骑手取餐、收款页面需逐子单显示付款方式,并单独合计到付待收金额,不能向用户再次收取线上已付餐费。线下收款路线仍沿用现行到付逻辑,不引入新资金池。
混用、逐笔选方式及统一15分钟发起/到付确认期限已确认。已就绪数与实际已付款数分别展示:到付确认参加就绪进度,不参与实收金额。支付失败/未知或未选方式仍不就绪;客户端回跳/已完成标志不能代替服务端事实。旧渠道尝试未决时不能直接切到到付并宣称就绪,须先核实/明确对账补偿,不直接复用旧切换接口或假定旧交易已经关闭。
新多店批次只允许当前真实可用的LINE Pay(payType=3)、OMG信用卡/Apple Pay(2/5)和到付(1),无论是否选择合并配送,均不允许转账(6)。本轮不新增待核款提醒、核款期限、转账人工退款或迟到转账补偿入口;此前候选兼容方案已撤下,不继续作为待实现内容。
每店可选集=该店现有闸门返回的就绪方式∩本批次支持方式;商家可不同渠道,不要求跨店交集,但每店都必须有至少一种本批次方式。过滤仅在新多店预结算/创建/换方式上下文,不关闭全局转账开关、不修改商家选择、不让现有StorePayMethodController的单店查询丢失转账。
仅支持转账或没有就绪支持方式的店,预结算时明确返回不能参加多店结算的原因;用户可以另走原单店流程,不能静默移除该店、自动拆成另一次结算或替商家改到付。统一提交重新校验所有店,防止配置变化或伪造请求;在创建任何可支付子单前拒绝非法方式,避免付前两家才发现第三家不能付。多店后续切换同样不得切到6。
现有单店payType=6、商家confirmTransferPayment及历史转账订单保持原流程;不因新的父批次闸门拦住非适用旧订单。回归覆盖单店转账下单、可用方式查询、商家核款和后续原履约。旧转账退款能力不足是已有单独议题,不能宣称本轮已经补齐,也不借此修改现有入口。
| 现有入口/结构 | 当前事实 | 本功能拟调整 |
|---|---|---|
| UserOrderController.createOrder | 一个 OrderParent + 多个 PosOrder;每子单 parentDdId 相同 | 新批次标识、全量预检、服务端算价、幂等创建与支付期限 |
| OrderParent.orderStatus | 0待支付、1已支付、2已取消;当前下单写0,尚无本批聚合机制 | 纳入到付后建议新增独立批次生效状态,不把“已支付”改成“到付已收款” |
| PosOrder.isDisplay/displayTime | 有预约显示用途,骑手列表使用 isDisplay | 保持展示职责;不单独承担付款与操作授权 |
| LINE Pay request | 明确拒绝多门店父单下子单支付 | 放行经过新批次校验的子单,继续取该店金额与凭证;不简单删除全部限制 |
| OMG create | ddId != parentDdId 的多店子单被拒绝 | 同上;保留所有订单归属、状态、金额和凭证校验 |
| 单店付款成功/到付创建通知 | 当前按子单成功直接进入新单通知,无批次就绪检查 | 本次已确认付款事实先保存,全部合法就绪才持久记录幂等开放事件;预约等旧开放时点,提交后发送并支持重试,部分付款不得提前履约通知 |
| 骑手 acceptOrder(id) | 一次只分配一个子单,容量按 pos_order 计数 | 新配送组原子分配;单笔入口拒绝拆抢;现有任务字典按组计1单/普通单计1单,统一外卖与跨类型总量,不新增子单总量上限 |
| 用户 cancelOrder(id) | UserOrderController.cancelOrder仅校验state=0,骑手接单更新deliveryStatus/qsId而不更新state,可能放行已承接单;LINE退款意图按单处理 | 新批次未生效只允许整批取消;已生效须校验组分配及子单qsId/deliveryStatus,已承接拒绝,未承接条件参加事务/CAS保护;不能绕过旧单笔入口。当前记录方案,不提前改普通订单 |
| 当前 OMG refund | 测试阶段拒绝所有退款,未提供正式退款执行链 | 新增正式退款编排、事实记录、查询/重试/人工处理与环境硬边界 |
| LINE Pay refundFull | 仅全额;每付款记录目前对应一条退款记录 | 已确认用户取消只发生在未接并全退,优先复用全额机制;异常售后如需部分退款另确认,不默认新增多次/部分退款扩展 |
沿用当前普通外送要求:批次生效且到达预约开放时间后开放抢单,骑手接组成功后商家才接单/出餐,不是先等各店出餐再让骑手看到,否则会与现有骑手先接单规则互相等待。即时单无预约时间等待;独立取餐/送达与组汇总按已确认Q11处理。用户明确多店目的为配送费优惠,除已确认批次支付/接组/收费规则外,不额外设计新的履约流程;本次独立履约不撤回已有批次就绪/超时取消决定。
预约批次共用15分钟发起/到付确认及快照缓冲,线上既有尝试可在缓冲完成和核实,不等预约当天付款;全部就绪后沿旧时点开放。本轮已批准普通平台配送和新多店预约后续扫描、重启补扫、幂等开放和通知重试,不新增预约语义或倒推最迟接单算法;037现有规则接入按FR-042。创建先设置type/delryTime再判旧显示条件,普通预约不套多店期限,夜市自配送/自取不纳入扫描。
2026-10-13已确认合并资格:即时单与预约单不能同组;预约单的日期、开始和结束时间必须完全一致才能同组,仅时段重叠、不同日期同钟点均不允许。预结算明确方式/时段不兼容,创建按服务端规范化的真实配送方式/日期/起止时间重验并拒绝不一致成员,不静默改时间、转即时或自动拆组。由于同组由同一骑手承接,不仅凭“都是预约”或时段交集放行。此决定不取消同一时段预约多店支持,也不提前启动下方预约时间改造。
记录日期:2026-10-12;2026-10-16更新范围。 新预约语义与倒推最迟接单算法仍延期;旧时点基本开放扫描、重启恢复、幂等通知及 037 既有无人接单规则的多店接入已批准纳入本轮,激活前预约过期检查按 FR-043。本节下列历史建议中的“最迟接单/自动截止”只指新的倒推算法,不再排除 FR-042 的既有规则接入,不能视为代码已实现。
后续仍从本节确认新预约语义与倒推最迟接单算法;旧时点基本扫描按本次已确认范围在7.1落实。当前只更新方案,不写业务代码、执行SQL、创建或切换分支,不声称扫描或新预约功能已完成。
原草案建议复用父单 orderStatus;用户纳入到付后,现采用独立的批次 checkout_status(PENDING/ACTIVE/CANCELLED,见 data-model),不改变原支付状态含义,不用“到付”冒充渠道付款成功:
| 新批次状态 | 子单真实付款事实 | 新批次履约资格 |
|---|---|---|
| 待生效 | 前15分钟可选方式/发起/确认到付;截止后仅既有线上尝试可继续完成付款和核实,未全部合法就绪仍待确认 | 全部禁止 |
| 已生效 | 所有真实成员均已线上可信付款成功或明确合法确认到付;确认到付不是实收 | 满足预约开放/售后/配送归属等规则后才可以 |
| 已取消 | 可以尚未付款、已付款待退款或已退款 | 全部禁止;后续成功付款不得重新生效 |
必须有新批次适用标识:以服务端写入的 order_checkout_member.pos_order_id 唯一关联判定,不能仅凭父单号、客户端字段或 isDisplay 判断。新多店到付属于适用范围;无该关联的历史/单店/旧到付订单继续原流程。现有到付子单 payStatus=1 是旧履约准入约定,不是渠道实收证据,退款及收款展示必须结合付款方式和真实交易。
2026-10-13用户明确兼容底线:不影响现有非多店订单。新标识必须由服务端新批次创建流程写入,不能由客户端自由指定;不能仅靠parentDdId非空、子单挂父单或数量判断适用。标识缺失的现有普通/历史订单、自取、夜市自配送沿原流程,不被新增15分钟期限、整批就绪、价格/营业策略、转账过滤或通知延迟影响。共享入口允许加合法新批次分支,但不得全局移除LINE/OMG原多店限制或更改原创建/取消规则。
isDisplay 不改变预约时间含义。骑手/商家待办列表同时过滤父批次生效条件,直接操作和推送再独立检查;不是“列表看不到就无法调用接口”。opened_at和事件OPENED;独立通知任务随后按组/子单推送并去重重试,取消后不开放。逐单回调/到付确认仅记事实并聚合,不在支付事务内推履约新单;用户真实支付结果通知及后续逐子单取餐/送达通知保持原职责。pos_order_invoice,各店出餐继续复用既有自动开票。幂等摘要包含发票字段,无新增 SQL 或 App 源码改动。建议新增一个轻量 order_delivery_group 表表示配送组,PosOrder 增加可空 deliveryGroupId;不新增收款账户表或跨商家资金池。不合并时不创建组。
建议数据职责:
用户已确认首版全局规则、不做城市/区域差异配置,最大合并门店数默认5且可配置。开关、范围、最大店数、动态折扣阶梯及支付完成/确认缓冲统一在配置页面管理;支付发起/到付确认期限固定15分钟,缓冲默认5分钟可配置,创建时固定两个截止。城市/区域覆盖留后续,本轮不加归属字段。最大店数在预结算/创建/接组重验,接单须满足原任务字典,不自动拆组,不新增子单总量上限或要求最大店数小于任务上限。
平台维护可增删改的“最少店数+折扣”列表,无需为后续4店七折等档位改代码或重新部署,不使用固定两档字段。2026-10-13用户明确要求不接 sys_config,改建 order_checkout_config 专表;固定参数列化,动态阶梯保存在 tiers_json,版本号用于报价重验和平台 CAS 保存,详见data-model与036 DDL。
2026-10-13用户确认撤回此前双层容量方案:不新增骑手实际子单总量上限、新字典或页面管理项,只沿用现有接单字典,并调整计数为合并组整体算1单。组内最大门店数由本功能配置独立控制,默认5家;这是单组规模,不是骑手所有子单合计限制。
配置职责:
| 配置 | 口径 | 本轮处理 |
|---|---|---|
| sys_rider_food_active_order_limit | 同时承接外卖任务数;单独订单和配送组各计1 | 沿用现有配置,不自动改值 |
| sys_rider_flash_active_order_limit | 同时承接闪送任务数,保持原义 | 沿用现有配置 |
| sys_rider_total_active_order_limit | 外卖任务+闪送任务总数 | 沿用现有配置,不自动改值 |
| 合并组最大门店数(复用本功能全局配置) | 一次合并最多多少店,可配置 | 默认5(已确认) |
例:外卖/总任务上限均为1、组门店上限为5,空闲骑手可以接一个三店或五店组,均占1任务,不能再接另一普通单或闪送。若外卖/总任务均允许2且其他条件满足,两个五店组可以接,按2任务计,不因子单合计10而额外拒绝。以上是计数示例,不是修改现有字典值的授权。
现有任务字典缺失、非法或读取异常均保守回退为1,即最多1个任务而不是组内最多1家店;合法五店组仍整体算1任务。合并门店数等配置缺失/非法时保守关闭合并报价,不回退为不限门店。接单按实时任务容量和门店上限再校验,不保证某位空闲骑手存在或接单;若组门店上限下调导致已创建待接组无法接,明确原因并交平台跟进,不追改收费快照,也不强制取消已接任务。不再校验每组最大店数与已撤回子单上限的大小关系。
容量释放:组完成或合法取消部分子单后,只要剩余有效成员仍在履约/处置,组就继续占1任务;最后一个有效成员结束才释放整个组任务,不存在逐子单释放名额。各单独立送达不等于整组结束,某单结算/通知不等待其他单。已取餐进入售后或异常不等于货物已交付,确认实际履约结束或交接处理前仍占组任务。实现不能沿用“afterSaleStatus不为0就不计容量”的简化过滤;异常处置终态按Q5后续范围确认,不因Q11独立履约而新增特殊业务状态。
验收至少覆盖:三店/五店组都算1任务、组与独立单分别计数、总任务和加急独占、普通单/闪送/接组并发。任务上限1时两组最多成功一个,任务上限2且其他条件满足时两个五店组可接;部分完成/取消不提前释放组任务、已取售后保持占用、字典异常回退1任务、组门店上限变更与历史非分组兼容。不得残留已撤回的子单数量拒绝条件。
配送费舍入(用户已确认):每店折后运费=该店原运费×命中折扣,在新台币元口径用十进制精确计算、HALF_UP到整数后固化;批次总运费=各店已舍入运费相加。不先对整批原运费总额打折舍入,不按比例分摊,不向任何店补齐尾差;撤下原比例分摊/稳定顺序补尾差建议。没有合并的单店仍沿原计费规则。
商家满额免运叠加(2026-10-19 用户确认):先得到逐店折后运费 R,再把 R 传给现有 008 完整配送促销算价作为免运基数;不以折前价重复补贴。算价结果固化 F=商家补贴、D=客户运费=R−F、A=优惠后餐费+D。原40八折后32且全免时,客户运费0、商家补贴32、骑手仍收32。建批重新算价并核对四项快照;普通单仍用原路径。
例如41元八折32.8元按33元;45元九折40.5元按41元。三店原运费各41元、八折后各33元,总运费99元,而不是总额123元八折98.4元再舍入为98元。即使各店原运费不同,也逐店按同一规则处理。仅配送费折扣按本条舍入,不额外修改商品价/优惠券等原有金额规则。
用户支付、真实原交易退款、商家给骑手的毛运费、到付总额减运费以及骑手账单计算基数统一使用已固化结果;佣金/税沿既有规则另算。允许阶段部分取消不重算剩余成员、不补差。实现前核对持久化/DTO与LINE/OMG的实际金额单位并明确转换(实体“分”的注释不能直接当成渠道单位),避免元/分错算;统一使用十进制运算,不能各端或支付渠道各自舍入造成不一致。
示例:A/B/C 原运费各40,三店八折后运费96,每店实付32,各店餐费加32分别向该店支付。这不是从某店账户抽钱转给另外一店。
各店配送毛收入按折后实付运费计算:上述每店32就是商家给骑手的运费,不仍给40,不新增平台或商家补足24的机制。配送折扣同步降低该单骑手毛配送收入;既有佣金/税另依原规则计算,不把32说成扣除佣金后的净收入。
设每子单应付总额为A、折后运费为F:
示例(不含其他优惠/附加项):各店餐费100、原运费40、折后运费32,每单总额132。若三店都是到付,骑手分别给商家100,向用户合计收396,商品交付款合计300,配送毛收入合计96;若A/B线上、C到付,仅C的132计入用户待代收,给C商家100,A/B商家分别给骑手32,不能把整个批次396再次代收。
接口/前端接入文档逐店区分“给商家金额”“商家给骑手运费”“用户到付待收”,金额方向不能混成一个组运费字段。沿用现有到付资金路线,不新增资金池。已取消成员不继续作为待收目标;若现金已发生,按售后规则独立记录追回/退款事项,不能仅重算组总额掩盖事实。
现有OrderService.setQishouBilling按子单freight计算佣金/税及账单并调用钱包入账,接入时须保持上述折后基数和原结算职责一致。现金交付、账单记录及钱包余额不得被解释为平台额外再支付一次同笔运费;钱包原结算用途及取消后的冲正范围需在实现时核对,不能仅改报价便宣称现金/账单已闭环。
不在本轮方案中默认增加自动最优路线/TSP。围栏资格已确认用首店到其他店的直线距离、默认1公里可配置;配送距离/预计时长仍用道路路线,不把直线围栏判断改成道路导航/计费。取餐顺序由骑手自行决定,不强制按选店顺序执行。地图仓库现有按序多点算路可作为能力参考,尚未接通本业务,不能据此新增强制路线编排或宣称已联通;普通子单导航沿原流程。
整批取消。每个已付款子单退完整实付金额,包含该单实付配送费;未付款取消,结果未知先补查。退款实际调用使用该子单对应商家凭证/交易,不能只改 payStatus。
统一15分钟是线上支付发起/方式改选期限,不是催到付提前交现金;到付已在建批时确认,默认5分钟可配置缓冲仅允许合法既有线上尝试继续完成付款和核实,不开放新改选。在途未知不能按未付款抢先取消,最终截止未就绪关闭,真实线上实收原路退,未收款到付不因payStatus=1造退款。已全部合法就绪生效后不再按结算期限取消;无人接单按FR-042接入037,不能用结算期限替代。
支付请求在发起截止前由服务端记录并实际发起,仅代表既有交易可在缓冲继续完成,不代表已经付款或可以开放履约;须在最终确认截止前核实对应真实收款。第15分钟后禁止新的支付发起/重试/方式切换/到付确认,合法既有渠道页面可能在该时点后完成付款,这是用户已接受的口径放宽。最终截止仍不就绪或用户已主动取消则关闭;本地关闭不能保证渠道不再扣款,迟到实收持续对账退款,不重新激活。
按阶段处理,不能全阶段套用原文“单店取消不退配送费”:
用户已确认:生效后在允许单店售后的阶段部分取消,剩余子单不补差,保留原运费/折扣快照,不因三单变两单而换九折,也不因仅剩一单恢复原价;线上不再补款,到付剩余子单应收/给商家金额不追加差额,骑手账单仍按各单原折后运费基数。未生效仍仅整批取消;取消后重新下单按新的实际组合报价。
例:三家均合法参与、原运费各40、三单八折后每单32,生效后未接时用户取消C,A/B仍各32,C完整实收含运费全退;如果骑手已接,用户不能取消C。商家/平台异常若允许退出后的其他成员仍不补差,其退款事实独立记录,不转嫁。
用户取消的“已接申请取消、商家审核、退餐款保留运费”候选方案已撤下,不能列入执行任务。若后续异常售后确需部分退款,再明确金额/记录/渠道能力;未确认前不默认实现。容量释放遵守5.1,不能借异常退款立即释放已取餐仍待处置的容量。
退款按实际事实记录。已确认的普通用户取消为全额;如果异常售后最终纳入部分退款,不能直接沿用payStatus=2并宣称全退,金额/类型和展示须独立表达。此扩展仍待异常范围确认,不视为本轮已定需求。
现有Online客户端使用v4;官方v4 Refund支持refundAmount,省略为全额。项目refundFull发空对象、每付款一条退款记录,可作为本次未接取消全退的基础,仍需接入批次取消、重复/迟到付款和未知结果对账。多次/部分退款不因旧稿默认保留运费而自动扩展;若异常售后确认需要,再追加金额、幂等业务号、累计对账和展示能力。
官方 退款/取消交易 说明 DoAction 仅正式环境可用,按交易状态选择动作,且不能把“接受请求”与银行已退回混同:
保留旧单店创建接口,新增多店预结算/批次创建/批次详情与取消入口,统一复用商家下单规则而非复制一套旧逻辑。下表描述职责,新增路径和请求字段以 API 契约 为准;已实现的 /quote 仍以现有 Controller/DTO 为准。
| 能力 | 输出/约束 |
|---|---|
| 多店预结算 | 首店锚点、同骑手本次参与店/圈外排除原因、即时/预约时段、参与店本批次可用方式(过滤转账)、原/折后运费、实付、报价版本;用户确认集合后提交 |
| 多店创建 | 逐店提交paymentMethods并核对报价可用方式,固化子单方式和两个截止;到付当场确认、全到付可创建即ACTIVE,但须另满足开放条件,预约沿原规则 |
| 逐子单支付/安全改选 | 线上仍按原LINE/OMG入口发起真实支付;待生效、前15分钟且该店从未有网关尝试时,专用接口可改选本店可用方式,到付改选即确认但不扣款。缓冲禁止新发起、重试或改选,合法既有线上尝试可完成付款及核实;路径见contracts |
| 批次详情/进度与用户列表 | 新批次已逐店选方式;部分付款、已付待整批生效及缓冲核实中均可见并恢复合法结算,历史未选记录也不漏单。展示真实付款与到付确认区别及截止;token校验归属,不能将payStatus=1误标可履约。App只交付文档 |
| 子单支付 | 复用 LINE/OMG 真实支付入口,放行合法新批次成员,不接受客户端金额/商家凭证 |
| 未生效整批取消 | 原子关闭批次,返回各退款真实进度,不承诺调用完成即到账 |
| 合并配送接单 | 一次接组,返回所有子单;旧单笔接单不得拆组 |
| 子单取餐/送达 | 沿用普通单笔入口及状态/权限/幂等校验,独立操作、独立结算/通知;不强制取齐,不新增整组送达/结算入口;同步组汇总但不连带修改其他子单 |
| 平台配置 | 四语言合并配置、最大门店数、动态最少店数/折扣阶梯、服务端确认缓冲(分钟,默认5)及校验;说明原任务字典组计1单,不增加子单上限/另一套任务值,不追改已创建快照 |
| 平台跟进退款 | 各店退款状态与人工处理入口,只能查询/执行有权限的操作,不能手工假设渠道退款成功 |
业务编排和网关外呼继续放 ruoyi-admin;领域/Mapper/持久化放 ruoyi-system,依赖方向不反转。所有 DTO 明确声明,业务校验与错误国际化按 AGENTS;Java 加注释,平台与必要商家 PC 文案四语言。App 只交付接入文档,由前端人员实现。
036 基础 DDL 已登记 updatesql/sql.md,未执行;FR-042~045 对应新增时间/地址等列仍需实施任务核对并补齐登记后由开发者手动执行。本轮文档收口不运行迁移、不连接业务数据库。现有无关修改保持原状。
以下为已确认范围,不再列为待确认业务问题;字段/Mapper条件/接口签名在data-model/contracts统一,当前未写业务代码。
| 审查项 | 实施落点 | 约束与验收 |
|---|---|---|
| 4 通知 | LinePayFactService、LinePayOrderNotificationService、OmgPaymentNotifyService、DeliveryOrderNotificationService及新批次到付确认 | 支付事实先保存,全部合法就绪后持久开放事件;提交后立即尝试开放即时/已到点批次,预约未到及失败由分钟任务补偿。组实际开放后独立通知消费者推送,组/独立单去重及失败重试;用户付款结果与履约通知分开;FR-036/SC-024 |
| 5 预约开放 | UserOrderController预约初始化、DateUtil旧时间判断、PosOrder查询/更新及新增有效扫描入口 | 普通平台配送/新多店预约旧时点补扫、重启恢复、开放/通知幂等及并发;先设预约字段,新批次额外已生效,普通不套批次期限。禁复用TestTask,不新增倒推最迟接单算法,不回写历史已开放;FR-037/SC-025及FR-042/SC-030 |
| 6 用户列表 | UserOrderController.orderList/applyTabFilter、UnifiedOrderQueryService、UnifiedOrderTab及批次详情 | 新批次已选方式,部分付款/已付待生效/缓冲核实可见并恢复合法结算;历史未选可见,真实已付与到付分开,不遗漏新批次Apple Pay;只改新批次分类,App只交文档;FR-038/SC-026 |
| 7 权威算价 | 新多店预结算/创建、OrderPromotionHelper及真实商品/规格/促销/原运费能力 | 不信客户端amount/freight/foodAmount及food价格,服务端查真实价格/归属/数量,计算优惠与折后实付;报价差异拒绝/重报不只警告,金额单位明确,支付/退款/到付/账单同快照;FR-039/SC-027 |
| 8 优惠券 | 新批次整批/合法子单取消、PromotionUserCouponServiceImpl及Coupon Mapper条件更新 | 只返取消成员券、原有效期不延长、过期不续期;原券/原子单绑定幂等及并发核销,迟到旧取消不释放新单券,返券不等于退款到账;FR-040/SC-028 |
| 9 归属 | 新批次/关联详情、进度、收银/取消及涉及新批次的getParentOrderDetail | token确定用户,校验父/子/组归属和关联,不能原样复用无userId过滤查询;商家/骑手限定现有授权,失败无款项/状态/返券副作用;FR-041/SC-029 |
兼容边界:明确批准普通平台配送预约补齐旧时点扫描,其他新期限/就绪/算价/返券只用于服务端标识的新批次;夜市自配送、自取不纳入扫描,普通价格/付款/取消等不全局改写。App只交接入文档,SQL后续只登记updatesql/sql.md供开发者手动执行。
以下区分已确认范围与剩余建议;OMG 正式退款以及即时/预约/到付范围已确认,不重复询问。
| 编号 | 问题 | 本版建议 |
|---|---|---|
| Q1 | 每店独立选择与混用 | 当前已确认建批时逐店选择LINE/OMG/到付;线上真实成功才就绪,明确选到付即确认,全到付可建批即激活,但实际可抢以开放为准。不能服务端默认到付。统一期限见Q6,转账见Q13 |
| Q2 | 首店围栏及圈外店的结算边界 | 已确认首店A为圆心、默认1KM可配置;圈内A/B参与本次合并结算,圈外C不建本批子单、不计金额/折扣、不等待其付款。预结算明确排除原因、确认成员后提交;C保留购物车另结算为接入建议,不自动建单。撤回C同批及有C整批禁合并两种建议 |
| Q3 | 折后运费及到付交付 | 已确认每店原40八折后32,商家给骑手32,不另补原价差额;到付给商家=该子单应付总额−该单折后运费,向用户代收该子单总额,配送毛收入为折后运费。佣金/税沿用原规则;逐店整数新台币舍入/不分尾差亦已确认见Q8,不重复询问 |
| Q4 | 合并组与骑手容量 | 2026-10-13最新确认:撤回双层/子单总量限制,只沿用现有外卖/闪送/总任务字典,合并组算1单、普通单算1单,原字典值不自动改写;组门店上限默认5可配置。资格、任务上限及门店数同锁校验,部分完成仍计1任务,最后有效成员结束才释放;原任务字典异常回退1任务,不新增子单字典/字段/页面项 |
| Q5 | 用户取消与异常处置 | 已确认未接可取消全退实收含运费、已接禁用户取消不审核;已接未取无法供餐由有权限商家取消本店,其他成员继续不补差。到付交餐才给钱不先垫,取消券按7.1返还;其余异常沿普通有效流程,不另建多店售后或默认空跑补偿 |
| Q6 | 支付发起期限、完成/确认缓冲与迟到结果 | 最新已确认15分钟线上支付发起/到付确认期限+默认5分钟平台可配置缓冲,创建固定两个截止/时长、重试不续时。截止前服务端已记录并实际发起的线上尝试可在缓冲完成用户认证、付款与确认,取代严格15分钟内完成付款的旧口径;缓冲禁新发起/重试/切换/到付确认。全部合法就绪立即生效,最终未就绪整批取消、真实实收全退含运费,取消后迟到实收退款不重开;不影响旧非多店,发起证据/真实收款/并发按4节适配验证 |
| Q7 | 动态折扣阶梯、默认合并数量、城市区域配置、距离口径如何定? | 已确认最少店数+折扣动态增删改、最高满足门槛匹配、每店打折且不追溯旧单;默认最多5家可配置,首版全局规则、不分城市/区域。初始最少2家九折/最少3家八折已确认,4家七折仅后续扩展示例不默认启用;首店到后续店使用直线距离、范围默认1公里可配置亦已确认,城市/区域覆盖后续另行评估 |
| Q8 | 部分取消是否补差?费用分摊/尾差如何舍入? | 已确认每店原运费独立打折、HALF_UP到整数新台币,再相加得总运费,不做整批舍入/比例分摊/尾差转移;支付/退款/到付/毛配送收入取同一快照。生效后允许阶段部分取消不补差、不重算剩余成员;取消单本身退款见Q5。元/分与渠道单位在实现前核对,不改变原商品价/优惠规则 |
| Q9 | 权威算价/快照与兼容 | 已确认创建服务端全量校验及真实商品/规格价格、优惠、原/折后运费和实付计算后固化快照;付款不复查普通菜单/营业变化,仍校验归属/状态/期限/方式/凭证。新批次取消券返还、原有效期和幂等已确认,不新增库存预占;普通单原规则不改,普通预约仅按Q12补扫 |
| Q10 | 支付工具与最终退款规则是否匹配? | 先沿用现有已支持LINE/OMG信用卡/Apple Pay工具,不新增分期等支付工具。用户普通取消现在只需未接全退,不再以“已接取消保留运费”强制要求部分退款或新增禁用;异常售后若确定部分退款再核对渠道能力。OMG正式退款仍已确认纳入 |
| Q11 | 骑手什么时候看到配送组,取餐顺序与履约如何定? | 已确认沿原外送:生效且达到预约开放时间后可抢单,接组后各店独立接单/出餐;独立取餐、独立送达,取餐顺序骑手自行决定。单笔结算/通知沿普通订单,不要求全组取齐,不新增整组送达/结算。组状态只汇总混合子单进度,最后有效成员结束才释放1任务;撤回全取齐后整组送达建议 |
| Q12 | 延期新预约算法与本轮基本扫描 | 即时/预约不同组,预约日期/起止完全一致,合法同段多店保留。本轮已批准普通平台配送/新多店外卖预约旧时点持久扫描、重启补扫、开放幂等及通知重试;新批次额外须生效,普通单不套新期限。新语义/倒推最迟接单仍后续,037现有规则的多店接入已于2026-10-16确认,基本扫描不延期 |
| Q13 | 线下转账范围 | 已确认新多店批次不支持转账,原单店/历史订单及商家设置保持不变;各店按LINE/OMG/到付就绪集合选择。仅转账门店提交前明确提示不可参与,可另走原单店;不静默删店/自动拆单/擅自转到付,不新增核款/转账人工退款 |
原业务规则及本次第4~9项均已确认,不重复询问:全部合法就绪后幂等通知、普通平台配送/新多店预约按旧时点补扫、用户列表/继续结算、服务端权威算价、新批次取消券返还、详情/进度/操作归属。仅新预约语义与倒推最迟接单继续延期,037接入按本次确认实施,其他异常沿原有效流程。下一步细化data-model/contracts及并发/测试映射供用户审阅,完整技术方案批准前不写执行tasks或业务代码,不把确认当作实现验证。
OMG 实现按项目纪律将每个接口的代码、测试源码、规格与 SQL 作为完整批次;不反复确认已确定事项。此处并非启动新的 OMG 重做测试禁令;实际测试时机在执行任务阶段按用户要求确定。
.specify 工作流有 specify/plan 审阅闸门。本轮按用户要求先输出规格与方案草稿;不运行会创建分支或自动提交的扩展脚本,不修改当前 .specify/feature.json(仍指向032,待进入本功能执行阶段再显式切换)。用户已确认以下四项建议,纳入本轮实施与验收;对应 spec.md FR-042~045、SC-030~033。此前确认的支付、取消、运费和自由取送规则不重开讨论,确认不等于代码已实现。
| 优先级 | 缺口与场景 | 已确认处理 |
|---|---|---|
| 高 | 037 当前只实现独立外送单无人接单取消,多店不会自动继承。 | 本轮接入现有阈值;计时基准取批次生效、首次实际开放和预约开始三者最晚,不能从子单先付款开始。未承接组原子取消剩余有效成员,逐交易退款;部分取消不重置,接组与取消互斥。无配送组的独立配送成员沿独立处理,仍以父批次统一生效作为基准。 |
| 高 | 预约时段在付款/缓冲期间过完,例如 14:00 建批、预约 14:05–14:15、14:18 才核实齐付款。 | 尚未生效批次激活时已到或超过预约结束则整批取消、退真实实收,不自动转即时;恢复扫描补处理,未知/迟到款继续核实退款。原付款两个截止不延长,不新增预约提前量算法;已生效任务不因本检查被取消。 |
| 中 | 子单改址会破坏同地址配送及收费快照。 | 建批后冻结地址与坐标快照,修改地址簿不改订单;首版不新增多店改址。未生效要改址先整批取消再报价,生效后沿已确认取消权限,不放开已接用户取消;旧单笔改址不能绕过。 |
| 中 | 最大店数 5 降 3 会使已建五店待接组无法承接。 | 保留实时接组重验;存在有效成员数超出新上限的未结束、未承接组时,拒绝该次下调,含尚未生效的已建组。检查与建批/配置版本并发一致;已接组继续履约,不改收费快照。 |
另列为实施验收、不再另设业务审批:共享的付款/换方式/取消/商家/骑手/预约扫描入口都识别新批次关联,避免旧入口提前履约或拆组;新批次退款编排复用 LINE/OMG 现有退款执行与交易幂等事实,不生成两套可独立执行的重复退款;App 新组合卡的能力兼容、列表分页和角标统一验证,未完成前不开放用户入口。其他已取餐异常仍按已确认的普通有效流程处理,但必须验证该流程能保留组任务直至实际履约/交接结束,不能只改售后状态后释放任务。
本节把 T020 的文档工作收口,供当前仓库继续执行 T003–T026;只定义目标,不宣称全部代码已实现。用户确认骑手到首店的距离作为整组待抢/接单范围基准,以及用户端、骑手端均须升级后使用多店功能。对组合卡的一批一张/一组一张、单一主页签、双进度和具体字段采用本轮实施方案,见 联合展示、API 契约、App 接入。
order_checkout_member 的真实关联进入新批次逻辑;未关联的普通/历史单继续原创建、付款、取消、接单与履约路径。预约旧时点扫描是本轮已授权的普通平台配送补齐项,不把 036 的 15 分钟期限或折扣套给普通单。共享入口逐个按关联分支测试,不能只改 App 列表。cardType/cardKey、逐店摘要、真实支付/退款、双进度与可操作性。用户 GET /user/cards、骑手 GET /rider/tasks 和角标共享过滤口径;新任务以首店到骑手距离排序和判断范围,接组在同一骑手锁内重新校验。旧外卖 Tab 名称和普通单顺序规则保留,不能让同组跨待取餐/配送中重复出现。batchId/orderId/groupId 放 body。旧单笔接口发现新批次成员时实施服务器侧防绕过,必要的逐子单履约继续沿原有效入口并在服务端校验实际骑手。新预约送达时段语义、空跑补偿和已取餐后的特殊异常售后仍是明确延期事项,不在本次编码范围内;本轮必须保证现有普通异常流程不会让仍需交接的组提前释放容量。
范围:FR-049~051、SC-037~039;沿当前分支,仅后端矩阵能力、两个报价服务、对应测试和文档。不修改旧/createOrder的多item边界、围栏定义、App源码、SQL或开关;不混入工作区已有多店实现修改,不自动提交推送。
ruoyi-admin/src/main/java/com/ruoyi/app/order/route/新增RouteDistanceMatrixProvider,方法List<BigDecimal> distancesToDestination(List<GeoPoint> origins, GeoPoint destination)。复用不可变GeoPoint,结果以起点原序返回正整数米,长度必须与起点数一致;失败抛异常,由报价边界转为原有国际化错误。未来自部署矩阵可实现该接口,本轮不虚构地图端点或把串联路线冒充矩阵。GoogleRouteDistanceMatrixProvider,POST https://routes.googleapis.com/distanceMatrix/v2:computeRouteMatrix;origins为门店,destinations仅收货地址,DRIVE/TRAFFIC_UNAWARE。复用sys_googlemap_key字典,不复制硬编码Key,不增加开关。沿已有HTTP组件风格采用HttpClient4,连接/池等待2500ms、读取3500ms、禁自动重试,资源按try-with-resources关闭。不记录密钥、坐标、完整上游响应。originIndex,destinationIndex,status,condition,distanceMeters;校验condition=ROUTE_EXISTS、status.code为0、目的索引0、起点索引合法且不重复;省略的protobuf索引默认0。数量完整才返回按索引排序的不可变列表;异常直接拒绝,不使用部分成功或直线兜底。坐标范围、非空、最多625条也在提供器边界验证;重复坐标仍保留独立门店位置。MultiStoreCheckoutQuoteService先完成资格、围栏和支付方式筛选,再向矩阵传参与门店。按下标把道路距离交给已有originalFreight,随后沿现有折扣、满额免运、支付方式与指纹逻辑;普通报价复核由原BatchService重新调用quote,仍独立重验。FoodDeliveryPromotionQuoteService通过同一提供器查询一个起点;保留其外送/自配送/自取/堂食及运价分支。Controller/公开DTO不改;现有受保护路线边界方法可继续封装调用及错误翻译,但不新增只供测试调用的生产方法。前端接入仅补充“配送距离和费用由服务端统一计算,前端展示报价结果,建批时仍复核”,参数和调用顺序不变。Google批量减少HTTP次数,并非按一次请求只收一次费用;新旧Google路线可能给出不同里程,真实开通、配额与抽样金额需部署联调,本轮不调用付费真实接口。
参考:Google矩阵接口、响应字段与默认值、计费。
T033仅修正MultiStoreCheckoutQuoteService的商品可售判断,与单店完整报价统一为to_examine=1且stacking_up=0;补充解释字段含义的注释。先用测试库只读数据复现203店9135/9136和168店9115/9078及规格43/15/7,再用回归测试覆盖审核通过、未通过及NULL。现有测试商品补齐真实审核状态,保留规格/促销金额核验。前端参数、国际化错误契约、数据库和已创建订单快照不变。
用户明确父单继续使用99开头的原外卖格式,禁止UUID。原MS+32位UUID再拼3位序号产生37位,超过测试库pos_order.dd_id的30位,也超过积分关联字段20位;通过修正生成源解决,不扩大数据库字段。
版本 2 订单共享的 OrderPromotionHelper.buildServerFoodJson 在完成规格归属/加价校验后,按规格 ID 读取数据库名称并写入兼容旧详情的 ask 与 specValueIds;不改报价、建批请求、旧订单保存逻辑或历史数据。用定向测试覆盖有加价和零加价规格,再运行受影响模块回归。
复用多店报价已取得的逐店道路米数,在建批重新报价时固化 pos_order.jvli 公里快照;组详情 members[] 从各子单快照返回 jvli,juli 与旧普通详情一致为空,不新增地图调用或计算组级单一距离。历史缺失保持空,报价金额及普通订单不变。