Files
medical-mall/docs/ops/2026-02-05__mall__delivery-merchant-customer-dataflow-analysis.md
2026-02-05 10:11:09 +08:00

390 lines
16 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.
# 2026-02-05 / mall配送-商家-消费者 数据流统一分析SQL + 项目规范口径)
## 摘要
本文基于本仓库 **权威 SQL 目录** `docs/sql/`(并参考 `mall_sql/` 中的 `complete_mall_database.sql` 实现细节),结合项目工程与安全规范 `docs/project_spec/AGENT_PROJECT_SPEC.md``docs/sql/11_roles_and_permissions_strategy.md`,对 **消费者 / 商家 / 配送员** 三方在电商场景中的数据流运转进行统一分析。
输出内容包括:
- 三方域模型与关键表关系(文字版 ER
- 端到端事件链路:下单、支付、履约配送、完成、评价
- 订单与配送状态机(字段口径与关键歧义点)
- 读模型(各端典型查询视角)
- 权限边界RLS / RPC / 前端守卫)在本项目中的统一口径
> 本文为分析文档,不包含代码/SQL 变更。
---
## 动机
- 统一“谁能看/改什么数据”的口径角色权威字段、RLS 与 RPC 的安全闭环要求必须一致。
- 统一“数据如何落表与流转”的口径:避免前后端/不同模块对订单、配送、商家模型理解不一致。
- 为后续落地(配置页、配送履约闭环、统计看板等)提供可审计的基础说明。
---
## 影响范围
- **数据库层**`docs/sql/` 中的表结构、触发器、RLS、RPC 策略口径(本文不修改)。
- **前端工程层**:页面必须通过 `services/` 访问数据admin/analytics 需通过 RPC 获取全局数据(本文不修改)。
- **业务模块**
- 消费者端consumer订单链路
- 商家端merchant商品与订单履约
- 配送端delivery接单/取货/送达
- 管理端admin配置与全局视角
---
## 变更清单
- **新增文件**
- `docs/ops/2026-02-05__mall__delivery-merchant-customer-dataflow-analysis.md`
- **修改文件**:无
- **删除文件**:无
---
## 权威输入(引用口径)
- 数据库与流程说明:
- `docs/sql/02_relationships_er.md`
- `docs/sql/03_enums_status_dict.md`
- `docs/sql/07_business_workflows.md`
- `docs/sql/11_roles_and_permissions_strategy.md`
- 工程与安全规范:
- `docs/project_spec/AGENT_PROJECT_SPEC.md`
- 参考实现(非权威口径,但含完整 DDL/触发器/RLS 示例):
- `mall_sql/schemas/complete_mall_database.sql`
- 管理端设置页现状(占位):
- `pages/mall/admin/system-settings.uvue`
---
## 1. 统一域模型:三方是谁(同一身份体系下的三类角色)
- **统一用户主表**`public.ak_users`
- **角色唯一权威字段**`ak_users.role`
- **商城用户扩展档案**`public.ml_user_profiles`
- 关系:`ak_users` 1:1 `ml_user_profiles``ml_user_profiles.user_id UNIQUE`
三方角色定义(业务口径):
- 消费者:`ak_users.role = 'customer'`
- 商家:`ak_users.role = 'merchant'`
- 配送员:`ak_users.role = 'delivery'`
结论:三方并非三套用户体系,而是“同一 `ak_users` + 不同域表扩展”。
---
## 2. 核心表关系(文字版 ER
### 2.1 消费者Customer
- `ak_users (customer)` 1:N `ml_user_addresses`
- `ak_users (customer)` 1:N `ml_shopping_cart`
- `ak_users (customer)` 1:N `ml_orders``ml_orders.user_id`
### 2.2 商家Merchant
- `ak_users (merchant)` 1:1 `ml_shops``ml_shops.merchant_id UNIQUE`
- 当前模型含义:一商家一店铺
- `ak_users (merchant)` 1:N `ml_products``ml_products.merchant_id`
- `ak_users (merchant)` 1:N `ml_orders``ml_orders.merchant_id`
### 2.3 商品Product
- `ml_products` 1:N `ml_product_skus`
- `ml_products` 1:N `ml_product_specs`
- SKU 变更触发汇总:`ml_product_skus` 变更 -> 触发器刷新 `ml_products.total_stock/available_stock`
### 2.4 订单Order
- `ml_orders` 1:N `ml_order_items`
- `ml_orders` 1:1 `ml_delivery_tasks``ml_delivery_tasks.order_id UNIQUE`
- 当前模型含义:一订单最多一个配送任务
### 2.5 配送Delivery
- `ak_users (delivery)` 1:1 `ml_delivery_drivers``ml_delivery_drivers.user_id UNIQUE`
- `ml_delivery_drivers` 1:N `ml_delivery_tasks`(通过 `ml_delivery_tasks.driver_id` 关联)
---
## 3. 三方端到端数据流(事件 → 落表 → 状态变化)
### 3.1 消费者链路(浏览→加购→下单→支付→收货→评价)
#### 事件 A维护地址
- 写表:`ml_user_addresses`
- 约束:同一用户最多一个默认地址
- 由触发器 `ensure_single_default_address()` 保证
#### 事件 B加购/改购
- 写表:`ml_shopping_cart`
- 约束:`UNIQUE(user_id, product_id, sku_id)`(便于 upsert 累加)
#### 事件 C下单创建订单 + 明细快照)
- 写表:
- `ml_orders`(订单主表)
- `ml_order_items`(订单明细快照)
- 关键设计点:
- `ml_orders.shipping_address` 为 JSONB 地址快照,避免订单与地址簿耦合
- `ml_order_items` 快照化写入商品名/规格/图/价格,避免商品变更影响历史
> 一致性提示(现状):当前权威文档指出未体现“扣库存/冻结库存”落地,库存一致性需由应用层事务或后续 DB/RPC 补齐。
#### 事件 D支付成功
- 更新:`ml_orders`
- `order_status: 1 -> 2`
- `payment_status: 1 -> 2`
- `paid_amount = total_amount`
- 触发器副作用:`handle_order_status_change()``1->2``paid_at`
#### 事件 E收货完成/订单完成
- 更新:`ml_orders`
- `order_status: 3 -> 4`
- `shipping_status -> 4`
- 触发器副作用:写 `delivered_at/completed_at`,并累计更新 `ml_products.sale_count`
#### 事件 F评价
- 写表:`ml_product_reviews`
- 强绑定来源:`order_id + order_item_id`
---
### 3.1.1 退款/取消在当前模型中的最小表达(现状与缺口)
当前数据库没有独立的退款申请表或售后流程表,只能通过 `ml_orders` 的状态组合表达:
- **退款分支**
- `payment_status: 3部分退款` / `4全额退款`
- `order_status` 通常进入 `6退款中` -> `7已退款`
- **取消分支**
- `order_status: 5`(歧义点,见第 4.2 节)
- `cancel_reason`(文本字段)可用于记录取消原因
> **缺口提示**
> - 缺少退款申请流水、审批流、支付对账表,复杂售后需后续扩表或接支付系统。
> - 缺少 `cancelled_at`、`refunded_at` 等审计时间字段。
> - 配送已取货/在途/已送达时的退款/取消口径未在当前模型体现(需业务层定义)。
---
### 3.2 商家链路(供给→接单→履约)
#### 事件 A创建/编辑商品
- 写表:`ml_products``ml_product_skus``ml_product_specs`
- 库存汇总SKU 触发器刷新 SPU 汇总库存字段
#### 事件 B发货/履约推进
- 更新:`ml_orders`
- `order_status: 2 -> 3`
- `shipping_status: 1 -> 2`
- 触发器副作用:`2->3` 时写 `shipped_at`
---
### 3.2.1 单商家订单模型的限制(需求提醒)
当前订单主表 `ml_orders` 直接持有 `merchant_id`,且未见“主单/子单”结构,因此该模型天然对应:
- **一笔订单仅归属一个商家/店铺**(单商家订单)
> 若产品需求为“一单多商家(多店铺)”,通常需要:
> - 引入主/子订单拆分(例如主订单聚合支付与收货信息,子订单按商家拆分履约与结算),或
> - 在下单阶段强制拆单(多个 `ml_orders`)并建立父子关联字段。
---
### 3.3 配送链路(派单/接单→取货→配送→送达)
#### 事件 A创建配送任务
- 写表:`ml_delivery_tasks`
- 关键字段:
- `order_id UNIQUE`
- `driver_id` 可空(先生成后指派)
- `pickup_address`(取货点)
- `delivery_address`(配送点,建议源自订单地址快照)
#### 事件 B接单/指派
- 更新:`ml_delivery_tasks`
- `status: 1 -> 2`
- `assigned_at` 写入
- 绑定 `driver_id`
#### 事件 C取货→配送→送达或失败
- 更新:`ml_delivery_tasks`
- `2 -> 3`(写 `picked_at`
- `3 -> 4`
- `4 -> 5`(写 `delivered_at`
- 失败:`status = 6` + `failure_reason`
> 一致性提示(现状):未看到 DB 侧“配送送达 → 自动联动订单状态”的权威触发器/RPC 设计,需在 service/RPC 层承担联动更新职责。
---
### 3.3.1 配送状态 → 订单状态的建议映射(统一口径)
为避免三方对“配送进展如何反映到订单状态”理解不一致,建议采用以下最小映射(可在 service/RPC 事务中实现):
| 配送任务 `status` | 建议同步的 `ml_orders.shipping_status` | 建议同步的 `ml_orders.order_status` | 说明 |
| ----------------- | -------------------------------------- | ----------------------------------------------------- | ------------------------- |
| 1 待接单 | 1 未发货 | 2 待发货(已支付) | 任务生成但未接单 |
| 2 已接单 | 2 已发货 | 2 待发货 | 商家/平台已指派,但未取货 |
| 3 取货中 | 3 运输中 | 3 待收货 | 骑手已取货,在途 |
| 4 配送中 | 3 运输中 | 3 待收货 | 仍在途 |
| 5 已送达 | 4 已送达 | 3 待收货(或直接 4 已完成,取决于产品是否需用户确认) | 骑手确认送达 |
| 6 配送失败 | 1 未发货(或自定义状态) | 2 待发货(或进入异常分支) | 需人工介入 |
> **关键决策点**`status=5` 时,`order_status` 是“待收货”还是直接“已完成”,取决于产品是否需要用户“确认收货”这一步。若不需要,可直接 `4`。
---
### 3.3.2 派单依据(任务池可见性)的字段来源
`ml_delivery_drivers``ml_delivery_tasks` 中的以下字段是“任务池筛选/派单算法”的数据基础:
- **服务区域**`ml_delivery_drivers.service_areas`JSONB用于按区域过滤可接单骑手
- **实时位置**`current_lat/current_lng`,用于距离排序/就近派单
- **工作状态**`work_status`(在线/忙碌/离线),用于过滤可用骑手
- **任务状态**`ml_delivery_tasks.status=1`,用于构建“待接单任务池”
> 权限提示:若任务池需对骑手可见,需配合 RLS 或 RPC 实现“仅可看同区域且状态=1 的任务”。
---
## 4. 状态机统一口径(必须统一的单一真相)
### 4.1 订单并行状态字段
`ml_orders` 存在三条并行状态线:
- `order_status`:订单流程
- `payment_status`:支付/退款
- `shipping_status`:发货/物流
推荐统一驱动关系:
- 支付成功驱动 `order_status` 进入“待发货”阶段
- 发货/配送接管驱动 `order_status` 进入“待收货”阶段
- 签收/确认收货驱动 `order_status` 完成
- 退款以 `payment_status` 为主线,同时影响 `order_status` 分支
### 4.2 `order_status = 5` 的歧义
文档指出 `5` 在不同脚本存在“已取消/已取货”表述差异。
- 无自提流程:建议统一为“已取消”
- 有自提流程:建议拆出独立状态值(避免取消与自提完成混用)
---
## 5. 读模型(各端典型查询视角)
> 本节描述“应该怎么查”,不涉及具体实现。
### 5.1 消费者端
- 商品列表/详情:优先基于商品详情视图(若存在)或 `ml_products`(上架商品)
- 我的订单列表:`ml_orders.user_id = 当前用户` + 时间倒序
- 订单详情:`ml_orders` + `ml_order_items`
- 配送进度:若需展示,则读取 `ml_delivery_tasks`(需权限策略或 RPC 支持)
### 5.2 商家端
- 我的商品:`ml_products.merchant_id = 当前商家`
- 注意:若 `ml_products` 的 RLS select 仅允许 `status=1`,商家后台查看草稿/下架需额外策略或 RPC
- 店铺订单:`ml_orders.merchant_id = 当前商家` + `ml_order_items`
- 配送任务看板:按订单关联 `ml_delivery_tasks`
> **现状风险RLS**`ml_products_select_policy` 仅允许 `status=1`,会导致商家后台直查表时看不到自己的草稿/下架商品。建议扩展 RLS 或为商家后台提供专用 RPC。
### 5.3 配送端
- 待接单任务池:`ml_delivery_tasks.status = 1`(通常需按服务区域过滤)
- 我的任务:`ml_delivery_tasks.driver_id = 当前配送员`
- 任务详情:最小化暴露订单/个人敏感信息(原则层面)
---
## 6. 权限边界RLS / RPC / 前端守卫的闭环)
### 6.1 前端工程约束
- 数据访问唯一入口:必须通过 `services/`
- 客户端路由守卫用于快速失败,但最终权限以数据库为准
### 6.2 admin / analytics 的全局数据访问
按项目规范与策略文档:
- 不建议对 admin/analytics 直接开放业务表全局 `SELECT`
- 应通过 RPC`SECURITY DEFINER` + 固定 `search_path` + 入口 `get_current_user_role()` 鉴权)返回最小必要字段/聚合数据
---
## 7. 金额字段的归因与缺口(运费/优惠/抽佣)
| 金额字段 | 数据来源 | 归因说明 | 现状缺口 |
| --------------------------- | ----------------------------------------------------- | ------------------------------------------------- | --------------------------------------- |
| `ml_orders.shipping_fee` | `ml_system_configs.shipping_fee`(或按距离/重量计算) | 配送费,可配置默认值或按规则计算 | 缺少运费规则表(如按区域/重量/距离) |
| `ml_orders.discount_amount` | 优惠券/活动/满减 | 优惠金额,使用券时关联 `ml_user_coupons.order_id` | 缺少活动/满减规则表,复杂优惠需扩表 |
| `ml_orders.total_amount` | 商品金额 + 运费 - 优惠 | 订单最终应付 | — |
| `platform_commission` | `ml_system_configs.platform_commission`(比例) | 平台抽佣,通常在结算时计算 | 缺少结算单/分账流水表,抽佣未落结算流水 |
> **结论**:金额归因在“配置 → 落表”层面基本可表达,但缺少结算/对账/复杂优惠的支撑表。
---
## 8. 系统配置system-settings与“配置驱动流程”的现状说明
- DB 已存在:`ml_system_configs`,并在参考脚本中初始化了配置项(如 `shipping_fee``platform_commission``order_auto_confirm_days`)。
- 管理端页面现状:`pages/mall/admin/system-settings.uvue` 目前为占位页,仅展示 query 参数,未接入配置读写。
结论:配置承载表存在,但尚未形成“配置 → 业务流程”的实际驱动闭环。
---
## 兼容性与风险
- **状态机风险**:三条并行状态字段若缺少统一约束,易出现互相矛盾的状态组合。
- **一致性风险**:库存扣减/冻结、配送送达与订单联动等关键一致性未在当前权威 SQL 中体现,需在应用层或后续 RPC/触发器补齐。
- **权限风险**:配送域 RLS/RPC 若未补齐,会导致配送端无法形成可运转的“接单→更新状态”闭环。
---
## 回滚方案
本文仅新增文档,无业务变更;删除该 markdown 文件即可回滚。
---
## 验证方式
- 打开本文档,核对引用路径均存在:
- `docs/sql/*``docs/project_spec/*`
- 与现有页面目录结构、角色字段口径(`ak_users.role`)一致。
---
## 关联文档
- `docs/project_spec/AGENT_PROJECT_SPEC.md`
- `docs/sql/02_relationships_er.md`
- `docs/sql/03_enums_status_dict.md`
- `docs/sql/07_business_workflows.md`
- `docs/sql/11_roles_and_permissions_strategy.md`