WATER_FULFILLMENT.md 7.5 KB

配送异常、重送与返仓结算

实施日期:2026-09-11。适用于平台后台供水配送,不执行支付、退款或外部消息发送。

业务规则

正常路径:待派单 → 已派单 → 配送中 → 已签收 → 路线返仓结算。

异常路径:配送中 → 部分签收/拒收(待退回核对)→ 仓库收回完好及破损货物(已退回)→ 路线返仓结算。已退回任务可为全部未送达桶数创建关联重送任务,重送重新走建单、派单、出库、签收流程。

  • 建单冻结权益和仓库可用满桶;出库由锁定转为在途。
  • 签收按实际收到桶数扣减权益和在途、增加企业满桶。实收0即拒收,部分/拒收必须填写原因。余量仍在途,权益仍冻结,有效任务键继续占用,防止绕过退货核对重复配送。
  • 回收空桶从企业账户扣除,暂记任务 empty_in_vehicle。有正式路线时,客户交空桶不代表仓库已入库。
  • 仓库核对要求 good_qty + damaged_qty = quantity - delivered_qty。完好满桶恢复可用,破损转入 sku.damaged_qty 隔离,全部未送达权益解除冻结,不扣除。任务变为 returned,释放有效任务键。
  • 返仓结算要求路线所有任务均为 completed/returned/cancelled,且逐规格 实际交空桶 + 差异损失 = 车载待交空桶。实交进入仓库空桶,损失累计至 empty_lost_qty,必须填写核对说明。禁止缺行、多行、重复规格或未解释数量差异。
  • 路线未结算时配送员持续占用,不能接新批次、休息、停用或改变容量/片区;结算后结合当日排班恢复可接单。结算不自动把休息或未排班人员改为上班。
  • 预警继续按企业现场满桶计算。部分签收可能解除数量预警,但待退回任务仍在调度待办;签收不自动模拟用水,下一轮现场消耗仍需人工换桶登记或未来设备协议。

设计和数据维护

  • WaterFulfillment:应用层命令服务,负责事务、锁、持久化和审计;统一承接旧 complete 入口的实际签收结算,移除 WaterBusiness 中重复的签收账务。
  • fulfillment/DeliveryState:显式状态策略;异常处理限定合法来源状态,禁止已出库直接取消。
  • fulfillment/DeliverySettlement:无数据库副作用的数量计算,分别计算实收及退回的库存、权益变动。
  • 继续使用 Eloquent 模型。WaterDelivery 提供原任务、重送及履约事件关联。redelivery_of 唯一索引保证每次失败尝试最多生成一个关联重送;重送再次失败可延续链条。新任务仍校验企业/规格启用状态、可用权益和可用库存。
  • WaterDeliveryEvent 由业务入口只追加,保存规范化请求、哈希、操作人、时间及账务快照。相同操作同内容重试成功,内容不同明确拒绝;不覆盖原签收数据。事件键由服务端动作和任务/路线ID组成,不依赖客户端随机值。
  • 加锁顺序以SKU → 配送员 → 配送任务 → 路线 → 权益账户为主,与既有派单/优化一致;同规格账务串行,整批返仓任一规格不一致则全量回滚。
  • 原库存流水查询/导出增加“部分签收/拒收、退回入库/破损隔离、返仓空桶交接”类型,履约详情同时展示对应路线结算事件。
  • 破损数量为隔离库存,不能再次派送;本轮没有自动报废、维修转可用或错误签收冲正入口。账务更正须另走可审计补偿流程,不能直接改旧事件。

API 与权限

POST /core/water/dispatch:

action 主要字段 权限
receive id, accepted_qty, empty_returned, receiver, reason dispatch:write
return_receive id, good_qty, damaged_qty, note dispatch:write + inventory:write
redeliver id(原任务), date, note dispatch:write
route_settle route_id, items:[{sku_id,actual,lost}], note dispatch:write + inventory:write

以上权限均带 water: 前缀,由 WaterController 核对启用账号、角色及菜单权限;不自动扩大代理权限。

  • GET /core/water/dispatch/history?id=...:dispatch:read,返回任务履约及路线交接记录。
  • GET /core/water/dispatch/route?id=...:新增 fulfillment(state/can_settle/empty_items)及 settled_at/by/note。
  • 看板新增 returning/returned 数量、重送关联、路线结算时间及 settlement_routes。待退回计入跨日待办,当天签收/退回的逾期任务继续留在当天列表供查询或重送;任务池桶数按剩余未送达桶数计算。返仓列表展示所选日期及之前全部未结算路线,明确不随任务池关键词/片区过滤,以免漏掉已结束任务的路线。
  • 任务详情展示计划与实收数、异常原因、重送父子编号和履约时间线;库存页展示破损隔离及累计空桶损失。

迁移、兼容与运行

在 server 目录执行 php -d extension=mbstring scripts/migrate_water_fulfillment.php --apply。默认不带 --apply 只输出计划。

增量增加字段、索引和事件表,不重算历史库存/权益,不伪造历史实收。历史已签收任务没有 received_at 时显示“历史已签收”,原签收重试不会再入账。旧 complete API仍兼容;正式页面使用需填写收货人的 receive。

迁移把具有已登记配送员、尚未关联路线的旧 assigned/transit 任务归档到独立 LEGACY-FULFILL-<任务ID> 路线,并保存系统迁移事件(actor_id=0)。本地任务10/11已关联,原状态、桶数、地址和资金/库存/权益不变。缺少已登记配送员时迁移明确中止数据关联事务,需先修复档案。重复执行不重复关联。内部历史服务测试中无路线的直接签收保留旧空桶入仓语义;所有当前实际在途任务已具备正式路线。

部署顺序:迁移 → 加载后端新代码 → 构建前端。当前本地HTTP进程已通过监控加载,8787实测新接口正常;前端4173提供生产构建。回退不能只换旧代码:新 returning/returned、车载空桶和未结算路线需要新逻辑,已产生新履约数据后禁止直接删除字段或回退到旧状态机。

验证与边界

  • 59项新增事务回滚检查:部分签收、全拒收、完好/破损退回、权益释放、跨日待办及当天退回保留、重复及冲突提交、单次重送/多次失败链、空桶短少、驾驶员占用、满桶/空桶守恒、多规格返仓失败全量回滚。
  • 69项原业务、42项调度、30项流水、30项设备接入、24项策略/模型检查通过;后端事务回归合计254项。
  • 19项真实本地HTTP检查跑通出库、部分签收、重复签收、退回、返仓、重送、取消及账务核对;临时专用SKU/企业/司机/任务等已清理,正常后台操作审计保留。
  • 本轮仍是后台人员登记实物事实:没有配送员移动端接单、客户电子签字/照片、实时GPS或自动设备核验,也没有支付退款、线上扣款。地图优化仍只按既有批次站序规划配送道路,未声称提供实时返仓导航。
  • 前端 vue-tsc、相关 ESLint/Prettier、Vite production build 已通过(3268 modules);道路矩阵/分段等既有前端测试通过。4173静态预览读取本次最终构建。
  • 浏览器自动化和备用桌面工具均因本机工具Node运行时路径缺失无法启动,本轮未完成视觉/交互验收。最终只读核对仍为4片区17单103桶、9条未结算正式路线(含旧任务10/11归档的路线35/36),未推进保留的演示任务。