Files
medical-mall/pages/mall/delivery/doc/需求文档/数据库对比与修改建议.md
2026-02-06 15:10:18 +08:00

197 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 配送端(自营骑手)表 vs 第三方快递轨迹表:对比与数据库修改建议
本文目标:
- 整理“原先配送端(自营骑手/同城配送)”使用的核心数据表。
- 对比“现在第三方快递配送(韵达/圆通等)”的轨迹/运单数据表。
- 给出建议的数据库改造方案:如何在不破坏订单主表的前提下,引入第三方快递轨迹能力,并(可选)逐步弃用原先配送端表的依赖。
明确决策(当前结论):
- 仅支持第三方快递轨迹(承运方运单 + 轨迹时间线)。
- 不做自营骑手端/同城配送任务流;`ml_delivery_drivers`/`ml_delivery_tasks` 视为历史遗留表,不再作为新能力的依赖与写入目标。
> 说明:本对比只讨论“第三方快递轨迹/运单”与“自营骑手任务”两套模型如何在数据库层共存/迁移;二者是不同业务域,不建议强行复用同一张表。
---
## 1. 原先配送端(自营骑手)核心表(现状)
数据来源:主库 DDL`mall_sql/schemas/complete_mall_database.sql`)与旧配送端文档(`pages/mall/delivery/doc/old/*`)。
### 1.1 `ml_delivery_drivers`(配送员信息表)
用途:记录配送员(骑手)档案与实时工作状态;用于配送端登录后的“是否可接单/是否在线”。
关键字段(节选):
- `id`:配送员 ID
- `user_id`:关联 `ak_users.id`
- `real_name``id_card``driver_license`
- `vehicle_type``vehicle_number`
- `service_areas`JSONB
- `work_status`1 在线 / 2 忙碌 / 3 离线
- `current_lat` / `current_lng`
- `rating_avg` / `rating_count` / `order_count`
- `status`1 正常 / 2 暂停 / 3 离职
### 1.2 `ml_delivery_tasks`(配送任务表,配送端“状态真源”)
用途:描述“一个订单被分配给某个骑手并经历接单→取货→配送→送达”的任务流。
关键字段(节选):
- 关联:
- `order_id`UUID**唯一**FK 到 `ml_orders.id`
- `driver_id`FK 到 `ml_delivery_drivers.id`,可为空表示未接单)
- 地址与费用:
- `pickup_address`JSONB取货地址
- `delivery_address`JSONB配送地址
- `distance``estimated_time``delivery_fee`
- 任务状态:`status`int
- 1 待接单
- 2 已接单
- 3 取货中
- 4 配送中
- 5 已送达
- 6 配送失败
- 时间戳:`assigned_at` / `picked_at` / `delivered_at`
- 其他:`delivery_code`(取货码)、`remark``failure_reason`
> 注意:旧页面/旧文档里常出现 `accepted_at`、`pickup_time`、`delivered_time` 等命名,和主库 DDL 的 `assigned_at/picked_at/delivered_at` 存在差异;如果你要继续使用自营配送链路,建议在代码层做统一字段映射或做一次字段对齐迁移。
### 1.3 与订单表的关系(旧模型的关键耦合点)
- `ml_delivery_tasks.order_id` 强依赖 `ml_orders.id`,并且 **1 个订单只能有 1 条配送任务**`order_id UNIQUE`)。
- 旧实现里常尝试把 `ml_delivery_tasks.status` 同步到 `ml_orders.order_status`,容易造成口径不一致(旧文档也提到了这种冲突风险)。
---
## 2. 现在第三方快递配送(轨迹/运单)表(目标模型)
数据来源:`pages/mall/delivery/doc/需求文档/express_tracking_mock_platform.sql`
> 这套表的定位不是“骑手任务流”,而是“第三方承运的运单 + 轨迹事件时间线”。
### 2.1 `platform_express_waybills`(平台侧:运单主表)
用途:一条运单(一个 `tracking_no`)的聚合信息;用于订单详情页展示“承运方/运单号/当前状态/最后同步时间”。
关键字段(节选):
- 关联:`order_id`(可选)、`order_no`(可选)
- 运单:`carrier``tracking_no``source`
- 聚合状态:`current_status_code``current_status_text`
- 时间:`eta``last_synced_at`
- 约束:`UNIQUE (carrier, tracking_no)`
特点:
- 同一个订单允许对应多条运单(拆包裹/分批发货)——因为 `order_id` 不唯一。
### 2.2 `platform_express_tracking_events`(平台侧:轨迹事件表)
用途:时间线的主数据来源;用于前端展示、告警统计与排障。
关键字段(节选):
- 关联:`waybill_id`FK 到 `platform_express_waybills.id`
- 事件:`event_id`(第三方可能缺失)、`event_time``event_code``event_text`
- 平台统一状态:`status_code`
- 节点:`node_name``location``description`
- 证据:`evidence_urls`jsonb
- 原始回文:`raw_payload`jsonb可用于审计/排障)
- 幂等:`dedupe_key` + `UNIQUE (waybill_id, dedupe_key)`
### 2.3 `platform_express_event_raw`(平台侧:原始接收表)
用途:记录 webhook/轮询的原始请求、验签与解析错误,便于排障与审计。
### 2.4 `mock_*`Mock 承运方侧表)
这部分只用于联调/回归/故障注入,不建议进入生产主库的核心业务 schema可以放在独立 schema 或测试库)。
---
## 3. 两套模型的核心差异(为什么不建议“复用一张表”)
| 维度 | 自营骑手(旧配送端) | 第三方快递(新模型) |
|---|---|---|
| 业务对象 | 任务(骑手接单、取货、送达) | 运单(承运方扫描轨迹) |
| 与订单关系 | 1 订单 = 1 任务(`order_id UNIQUE` | 1 订单 = N 运单(拆包裹常见) |
| 状态来源 | 平台自己驱动状态机(按钮/操作) | 第三方事件驱动webhook/轮询) |
| 数据颗粒度 | 少量节点(接单/取货/送达) | 高频节点(到站/发车/分拣/派送/签收…) |
| 关键字段 | `driver_id`、地址 JSON、配送费、距离 | `carrier``tracking_no`、事件时间线、幂等去重 |
| 风险点 | 并发抢单、任务状态与订单状态不同步 | 乱序/重复事件、验签、防重放、脱敏 |
结论:
- 旧表(`ml_delivery_*`)适合“自营骑手/同城配送”。
- 新表(`platform_express_*`)适合“第三方快递轨迹”。
- 两者可以共存,但不要强行把第三方轨迹塞进 `ml_delivery_tasks`
---
## 4. 推荐的数据库修改方案(兼容、可渐进)
### 4.1 方案 1推荐新增第三方快递表不改旧配送表
适用:你们当前要做第三方快递展示,且已明确“不做自营骑手”。
做法:
1) 在主库新增(或通过 migration 引入)三张平台侧表:
- `platform_express_waybills`
- `platform_express_tracking_events`
- `platform_express_event_raw`
2) 商家发货时写入/绑定:创建 `platform_express_waybills` 行(填 `order_id``order_no``carrier``tracking_no`)。
3) 第三方回调/轮询入库:写 `platform_express_tracking_events`,并更新 `platform_express_waybills.current_status_*``last_synced_at`
建议的小幅增强(强烈建议做):
-`platform_express_waybills(order_id)` 增加索引(便于按订单查运单)。
-`platform_express_waybills.order_id` 建外键到 `ml_orders(id)`(如果你们能保证订单一定存在)。
### 4.2 方案 2逐步“弃用旧配送端表”的依赖当你们全面第三方快递时
适用:你们不再提供自营骑手配送,或者想把骑手端做成独立项目。
做法(建议按阶段,不要硬删表):
- 阶段 A停止业务写入 `ml_delivery_tasks`(页面/接口不再创建任务)。
- 阶段 B订单详情页的物流展示只依赖 `platform_express_*`
- 阶段 C`ml_delivery_tasks` 标为 legacy只读保留一段时间最终再评估是否归档/删除。
为什么不建议立刻删:
- 历史数据、结算对账、纠纷复盘可能仍需要旧数据。
在“不做自营骑手”的前提下,最小要求:
- 新增第三方快递表后,业务代码只写入/读取 `platform_express_*`,不要再创建/更新 `ml_delivery_tasks`
- 旧表保留只读(或仅用于历史查询/清理脚本),后续再评估归档策略。
### 4.3 如果两种配送方式要并存(同城骑手 + 快递)
建议用“履约类型”做分流,而不是混表:
- 同城/自营:继续用 `ml_delivery_tasks`(任务流)
- 快递:用 `platform_express_*`(运单轨迹)
- 订单详情页按订单的履约类型选择展示模块(或同时展示多个包裹)
---
## 5. 迁移/改库执行清单(建议写成 migration
### 5.1 建表落库
-`express_tracking_mock_platform.sql` 的 A 部分platform整理为一份 migration放到 `mall_sql/migrations/`),避免:
- 重复创建 `set_updated_at()`(项目若已有同名函数)
-`mock_*` 测试表混进生产 schema
### 5.2 对接 `ml_orders`
- 建议:
- `platform_express_waybills.order_id REFERENCES public.ml_orders(id)`
- `CREATE INDEX ... ON platform_express_waybills(order_id)`
- 不建议:
- 把运单号/承运方直接塞进 `ml_orders`(会导致一单多包裹很难处理)
### 5.3 数据回填(可选)
如果你们历史上已经保存过“承运方/运单号”在别处(例如订单扩展表/发货日志),可做一次性回填:
- 按订单生成 waybill 行
- 再按运单触发一次轨迹拉取
### 5.4 权限与隐私
- 买家/商家/客服看到的轨迹必须“同源”,但展示要按角色脱敏;敏感字段(如手机号、原始回文)按文档约束控制。
---
## 6. 你接下来要怎么改数据库(最短路线)
如果你现在的目标是:商家可选承运方 + 录入运单号 + 用户能看轨迹时间线。
最短路线:
1) 在主库落 `platform_express_waybills` / `platform_express_tracking_events` / `platform_express_event_raw`
2) 发货绑定时写 `platform_express_waybills(order_id, order_no, carrier, tracking_no)`
3) 第三方事件入库写 `platform_express_tracking_events`
4) 订单详情页按 `order_id` 查 waybills再查 events 渲染时间线
---
(如你确认要我进一步把“建议的小幅增强”直接写成 SQL migration 文件,我可以在 `mall_sql/migrations/` 里新增一份只包含 platform 表的 migration并确保不引入 `mock_*` 表。)