本文拆清三类名称相近但生命周期不同的业务:首配方案生成首批配货采购单;预订单先支付定金或预付款,再分次提货生成采购单;旧预购表仍保留历史查询和兼容逻辑。
1. 业务目标
- 为新站或指定站点按经营方案生成首批商品清单。
- 将首配方案、基础包、商品和供应商转成正式采购订单。
- 支持普通和拼团预订单活动。
- 预订单下单时锁定商品、数量、折前/折后价和定金规则。
- 支付完成后按活动提货窗口分批提货。
- 每次提货生成来源可追踪的采购单。
- 采购关闭、退货或售后时归还可提数量。
- 全部提货后完成剩余退款或结算并关闭生命周期。
- 兼容旧
t_scm_preorder*与新t_pre_order*数据。
2. 核心概念
| 概念 | 业务对象 | 最终结果 |
|---|---|---|
| 首配方案 | t_bs_delivery_plan 及模板商品 | 直接生成首批配货采购单 |
| 微仓首配 | 微仓首配清单和销售转换 | 可能转销售单/补货链,非本文首批采购完全同义 |
| 新预订单 | t_pre_order / t_pre_order_info | 支付后分次生成采购单 |
| 旧预订单 | t_scm_preorder* | 历史预购、支付、提货和核销兼容 |
| 预订单活动 | 活动主数据 + t_pre_order_activity_settings | 定义定金、最小提货、提货窗口和拼团 |
| 提货 | 从预订单额度中选择商品和批量 | 创建一张采购订单 |
| 首配退货 | 采购退货类型 30-Cxx-18 | 退首配采购商品 |
3. 业务总图
flowchart TD
subgraph First["首配链"]
A["首配方案/模板/商品"] --> B["选择方案、仓库和数量"]
B --> C["按直发/非直发/广宣品分组"]
C --> D["首批配货采购单 30-Cxx-09"]
D --> E["支付/出库/入库"]
E --> F["首配退货 30-Cxx-18"]
end
subgraph Pre["新预订单链"]
G["预订单活动配置"] --> H["创建 t_pre_order 主从"]
H --> I["订单中心预订单"]
I --> J["支付定金/预付款"]
J --> K["待提货"]
K --> L["一次或多次提货"]
L --> M["预订采购单 srcOrderType=12"]
M --> N["采购履约"]
N --> O["关闭回退/售后/退款/完成"]
end
4. 代码地图
4.1 新预订单
| 路径 | 责任 |
|---|---|
application/controllers/scm/InvPre.php | 活动、预订单、提货、支付和页面接口 |
application/service/scm/InvPreService.php | 新旧预购页面编排和兼容逻辑 |
application/Services/PreOrders/PreOrderService.php | 新预订单领域主服务 |
application/Services/PreOrders/PreOrderCheckService.php | 是否可提货校验 |
application/Services/PreOrders/PreOrderStatisticService.php | 拼团、已提金额等统计 |
application/Services/PreOrders/PreOrderNotifyService.php | 订单中心售后、取消、关闭和退款回调 |
application/controllers/tasks/PreOrder.php | 超时取消、完成、通知等周期任务 |
application/models/orders/PreOrderModel.php | 新预订单主表 |
application/models/orders/PreOrderInfoModel.php | 新预订单明细 |
4.2 首配
| 路径 | 责任 |
|---|---|
application/controllers/scm/InvPo.php | 首配页面及采购入口 |
application/service/scm/InvPoService.php | 首配方案、商品分组、订单校验和采购落单 |
application/Services/FirstMatch/FirstMatchSer.php | 首配方案商品和规则 |
application/controllers/moveMall/FirstMatch.php | 微仓首配接口 |
public/js/api/firstMatch.js | 首配前端 API |
public/js/page/firstMatch.js | 方案选择、数量和确认交互 |
5. 表结构地图
5.1 新预订单
| 表 | 用途 | 关键字段 |
|---|---|---|
t_pre_order | 新预订单主表 | pre_order_no,order_status,refund_status,order_qty,pickedup_qty |
t_pre_order_info | 商品快照和逐行提货量 | inv_id,sku_id,qty,pickedup_qty,price,amount |
t_pre_after_sale | 预订单售后 | 售后单、数量、状态、退款 |
t_pre_order_province | 活动可提货省份 | activity_id,province_code |
t_pre_order_activity_settings | 定金和提货配置 | 类型、定金、最小批量、最大次数 |
t_pre_activity_group_level | 拼团阶梯 | 版本、阶梯、折扣 |
t_pre_activity_group_level_detail | 拼团固定商品价 | SKU、阶梯固定价 |
5.2 旧预订单
| 表 | 用途 |
|---|---|
t_scm_preorder | 旧预订单主表 |
t_scm_preorder_info | 旧预订单商品 |
t_scm_preorder_pay | 旧支付记录 |
t_scm_preorder_spend | 旧提货记录 |
t_scm_burning_bill | 旧核销单 |
t_admin_preorder* | 运营端旧预订单、商品和服务站配置 |
5.3 首配
| 表 | 用途 |
|---|---|
t_bs_delivery_plan | 首配方案 |
t_bs_setout_template | 首配基础包模板 |
t_bs_setout_template_item | 方案与模板关系 |
t_bs_setout_goods | 模板商品和数量 |
t_scm_po_order* | 最终首批配货/预订采购单 |
6. 单据关系
erDiagram
SALE_ACTIVITY ||--|| PRE_ORDER_SETTING : configures
SALE_ACTIVITY ||--o{ PRE_ORDER : creates
PRE_ORDER ||--o{ PRE_ORDER_INFO : contains
PRE_ORDER ||--o{ PO_ORDER : picked_as
PO_ORDER ||--o{ PO_ORDER_INFO : contains
PRE_ORDER ||--o{ PRE_AFTER_SALE : refunds
DELIVERY_PLAN ||--o{ SETOUT_TEMPLATE_ITEM : contains
SETOUT_TEMPLATE ||--o{ SETOUT_TEMPLATE_ITEM : selected_by
SETOUT_TEMPLATE ||--o{ SETOUT_GOODS : contains
DELIVERY_PLAN ||--o{ PO_ORDER : creates_first_order
预订单和采购单不是同一张单:一张预订单可以多次提货,对应多张采购单。
7. 订单类型字典
7.1 采购 orderType
| 类型 | 值 | 含义 |
|---|---|---|
| 预订采购 | 30-Cxx-08 | 由预订/提货产生的采购 |
| 首批配货 | 30-Cxx-09 | 首配方案采购 |
| 首配退货 | 30-Cxx-18 | 首配商品采购退货 |
| 普通采购 | 30-Cxx-01 | 非首配/预订单普通采购 |
| 预售专属授信 | 30-Cxx-71 | 新预售授信订单 |
7.2 采购来源 srcOrderType
| 值 | 含义 |
|---|---|
| 0 | 普通来源 |
| 1 | 活动 |
| 12 | 新预订单 |
排查采购单时必须同时看 orderType 与 srcOrderType。仅看 30-Cxx-08 不能证明一定来自当前新预订单实现,历史链路可能复用类型。
8. 新预订单状态机
枚举:PreOrderEnums。
| 状态 | 值 | 含义 |
|---|---|---|
| 待支付 | 1 | 已提交,等待支付 |
| 待提货 | 2 | 支付成功,可等活动窗口 |
| 提货中 | 3 | 至少一次提货,尚未全部提完 |
| 全部提货 | 4 | 枚举存在,实际完成逻辑需结合任务确认 |
| 已取消 | 9 | 用户取消 |
| 超时自动取消 | 10 | 待支付超时 |
| 已关闭 | 11 | 退款/活动失败/外部关闭 |
| 已完成 | 12 | 全部提货且退款/结算完成 |
stateDiagram-v2
[*] --> ToPay: 创建预订单
ToPay --> ToPick: 支付成功
ToPay --> Cancel: 用户取消
ToPay --> AutoCancel: 支付超时
ToPick --> Picking: 第一次提货
Picking --> Picking: 后续分批提货
Picking --> PickFinish: 已提数量达到订购数量
ToPick --> Closed: 售后/活动失败/外部关闭
Picking --> Closed: 剩余额度关闭
PickFinish --> Finished: 退款或最终结算完成
Closed --> Finished: 特定退款完成分支
Cancel --> [*]
AutoCancel --> [*]
Finished --> [*]
代码中部分方法在数量相等时仍从 STATUS_PICKING 扫描,再由完成任务推进 STATUS_ORDER_FINISH,实际是否使用 STATUS_PICK_FINISH=4 需以生产数据确认。
9. 退款状态
| 状态 | 值 | 含义 |
|---|---|---|
| 无退款 | 0 | 正常可提货 |
| 退款中 | 1 | 不允许继续提货 |
| 退款完成 | 2 | 根据来源关闭或完成 |
退款来源:
| 值 | 来源 |
|---|---|
| 1 | DGJ 全部提货后自动退款 |
| 2 | OPS 内勤发起 |
| 3 | 拼团失败 |
OPS 退款完成时 Model 会将主单状态更新为已关闭。
10. 售后状态
| 状态 | 值 | 含义 |
|---|---|---|
| 已提交/待审核 | 1 | 售后已创建 |
| 审核通过/待退款 | 4 | 等资金退款 |
| 已取消 | 8 | 售后取消 |
| 退款完成 | 9 | 售后结束 |
stateDiagram-v2
[*] --> Submitted
Submitted --> Checked: 审核通过
Submitted --> Canceled: 取消
Checked --> Finished: 退款完成
Canceled --> [*]
Finished --> [*]
11. 活动提货状态
| 状态 | 值 | 含义 |
|---|---|---|
| 未开始 | 0 | 不能提货 |
| 已开始 | 1 | 可按规则提货 |
| 已结束 | 2 | 不再允许新提货 |
| 预提货 | 3 | 预提货阶段,状态文案表未完整映射 |
拼团状态:进行中 0、成功 5、失败 9。
12. 定金方式
PreOrderSettingEnums 中 deposit_type:
| 值 | 文案 | 定金算法 |
|---|---|---|
| 0 | 固定金额比例 | handsel_amount = 订单金额 × handsel_payrate |
| 1 | 固定单价 | deposit_total = 订购批数 × deposit_price |
flowchart TD
A["活动配置"] --> B{"deposit_type"}
B -->|按金额 0| C["订单折后金额 × 定金比例"]
B -->|按件数 1| D["总批数 × 每批定金"]
C --> E["handsel_amount(分)"]
D --> E
预订单主表金额以分存储,列表格式化时除以 100。
13. 创建预订单
13.1 入口
Controller 方法:scm/InvPre::createPreOrder();核心最终进入 PreOrderService。
13.2 主流程
sequenceDiagram
participant UI as 预售活动页面
participant InvPre as InvPre Controller/Service
participant Pre as PreOrderService
participant Setting as 活动配置
participant DB as t_pre_order*
participant ODC as 订单中心
UI->>InvPre: 活动、计划、模板、商品和数量
InvPre->>Pre: submit/createPreOrder
Pre->>Setting: 查询定金和最小提货配置
Pre->>Pre: 校验活动未结束、总批数>=最小批量
Pre->>Pre: 从采购 Entry 复制主从快照
Pre->>DB: 事务写主表和明细
Pre->>ODC: submit 预订单
ODC-->>Pre: odc_order_no
Pre->>DB: 回写外部单号
Pre-->>UI: pre_order_no
13.3 主表快照
| 字段 | 来源/含义 |
|---|---|
pre_order_no | 本地生成 |
source_type | 活动预订单=1 |
source_id | 活动 ID |
source_order_no | 上游来源单号 |
order_status | 初始待支付=1 |
min_pickup_type | 由活动销售类型映射 |
activity_plan_id/template_id | 下单时计划和模板快照 |
order_qty/order_amount | 明细汇总 |
batch_num | 总订购批数 |
handsel_amount | 定金 |
13.4 外部调用风险
订单中心 submit() 在本地数据库 transaction 回调内部调用。外部提交成功后,本地事务若回滚,会形成订单中心有单、本地无单;重试还可能重复创建。必须依赖外部幂等键 pre_order_no 或补偿查询。
14. 明细金额和数量
formatPreOrderInfoList() 计算:
order_qty = Σ detail.qty
order_origin_amount = Σ(detail.qty × origin_price)
order_amount = Σ(detail.qty × price)
明细字段:
| 字段 | 含义 |
|---|---|
qty | 订购数量 |
pickedup_qty | 已用于提货生成采购单的数量 |
origin_price | 折前单价,分 |
price | 折后/拼团最终单价,分 |
amount | 折后金额,分 |
is_gift | 是否赠品 |
15. 拼团价
flowchart TD
A["统计活动总参团批数"] --> B["匹配有效阶梯"]
B -->|无阶梯| C["group_price = 原折后价"]
B -->|固定价活动| D["按 SKU 读取阶梯 fixed_price"]
B -->|折扣活动| E["price × level_discount_rate / 100"]
D --> F["group_amount = group_price × qty"]
E --> F
C --> F
F --> G{"是否覆盖预订单价格"}
G -->|是| H["price/amount 使用拼团价"]
G -->|否| I["只用于展示/统计"]
拼团阶梯变化可能改变最终价格。必须明确锁价时点:下单、成团、提货还是退款。代码支持在某些格式化场景覆盖原价,生产业务口径待确认。
16. 支付
采购支付服务的场景码 senceCode=preOrder:
sequenceDiagram
participant UI as 预订单支付页
participant Po as InvPoService
participant Pre as PreOrderService
participant Pay as 支付中心
UI->>Po: getPayInfoNew(senceCode=preOrder, orderNos)
Po->>Pre: 查询预订单
Po->>Po: 每单必须 STATUS_TO_PAY
Po->>Pay: 创建支付请求
Pay-->>Po: 支付参数
Pay-->>Pre: 支付成功回调
Pre->>Pre: order_status 1 -> 2,写 pay_time
首配 0 元订单代码明确无需支付;首配与普通采购同批支付还有前端提示“同批次一起支付/取消支付”。批次字段和支付中心幂等需结合采购专题检查。
17. 可提货判断
页面 canPick 同时要求:
- 活动计划当前允许提货。
order_qty > pickedup_qty。refund_status=0。- 主单状态为待提货或提货中。
flowchart TD
A["预订单"] --> B{"计划可提货"}
B -->|否| X["不可提"]
B -->|是| C{"仍有未提数量"}
C -->|否| X
C -->|是| D{"无退款"}
D -->|否| X
D -->|是| E{"状态=待提货/提货中"}
E -->|否| X
E -->|是| F["canPick=true"]
18. 最小提货规则
| 类型 | 枚举 | 校验对象 |
|---|---|---|
| 按批量 | 1 | 本次提货批数不低于最小批量,并满足商品最小起订量 |
| 按金额 | 2 | 本次提货金额不低于配置 |
活动还可能限制:最大提货次数、提货省份、计划时间、每商品剩余数量和拼团是否成功。
19. 创建提货采购单
Controller:scm/InvPre::createPickUpOrder();Service:PreOrderService::createPoOrder() / createPoOrderNew()。
sequenceDiagram
participant UI as 提货页面
participant Check as PreOrderCheckService
participant Pre as PreOrderService
participant Info as t_pre_order_info
participant PO as 采购服务
participant ODC as 订单中心
UI->>Check: 校验状态/窗口/退款/次数/数量
UI->>Pre: preOrderNo + entries + delayData
Pre->>Info: 锁定/读取可提商品
Pre->>PO: 创建 orderType=预订采购, srcOrderType=12
PO-->>Pre: 采购单号和明细
Pre->>Info: pickedup_qty += 本次提货量
Pre->>Pre: 主单已提数量/次数/批数累加
Pre->>ODC: 同步提货数量
Pre-->>UI: 采购单结果
20. 数量账
20.1 主单
| 字段 | 含义 |
|---|---|
order_qty | 预订单总订购件数 |
pickedup_qty | 累计已提件数 |
pickedup_times | 提货次数 |
batch_num | 总订购批数 |
picked_batch_num | 已提批数 |
picked_num_true | 真实提货数量 V2 口径 |
20.2 明细
明细未提数量 = qty - pickedup_qty
主单未提数量 = order_qty - pickedup_qty
可提商品条件 = detail.pickedup_qty < detail.qty
20.3 守恒
预订单订购数量
= 当前累计有效提货采购数量
+ 已关闭/退回到预订单的数量
+ 尚未提货数量
每个明细:0 <= pickedup_qty <= qty
主单 pickedup_qty ≈ Σ明细 pickedup_qty
picked_num_true 与 pickedup_qty 的差异语义需要根据 V2 业务确认,不能直接互换。
21. 原子更新
PreOrderInfoModel::batchIncryOrderInfoPickedQty() 使用表达式:
正数:pickedup_qty = pickedup_qty + abs(qty)
负数:pickedup_qty = pickedup_qty - abs(qty)
它支持提货增加和关闭回退,但必须在同一事务内校验上下界。单纯原子加减不能阻止并发两次提货合计超过 qty,除非更新条件包含 pickedup_qty + :delta <= qty。
22. 提货采购关闭回退
当预订单来源采购单关闭/退回时,returnPickedQty() / V2 会:
- 根据采购明细构造需要归还的预订单数量。
- 明细
pickedup_qty减少。 - 主单累计已提数量减少。
- 根据是否整次关闭决定是否归还
pickedup_times/picked_batch_num。 - 同步订单中心提货数量。
sequenceDiagram
participant PO as 预订采购单
participant Pre as PreOrderService
participant Detail as 预订单明细
participant Main as 预订单主表
participant ODC as 订单中心
PO->>Pre: 关闭/退回采购数量
Pre->>Detail: pickedup_qty -= returnQty
Pre->>Main: pickedup_qty -= totalReturn
opt 整次提货作废
Pre->>Main: pickedup_times/picked_batch_num 回退
end
Pre->>ODC: 同步最新提货数量
重复关闭通知如果没有来源采购单幂等记录,会重复归还额度,导致负数或可重复提货。
23. 订单中心同步
新预订单创建、提货数量变化、售后和关闭都与订单中心交互。
| 事件 | DGJ 行为 |
|---|---|
| 预订单提交 | 调用 PreOrderProvider::submit,回写 odc_order_no |
| 提货变化 | syncPreOrderPickQtyToOdc |
| 售后创建 | PreOrderNotifyService::createPreRefundOrder |
| 售后取消 | cancelPreRefundOrder |
| 预订单关闭 | closePreOrder |
| 退款完成 | preOrderRefundFinish |
数据库事务无法回滚订单中心,需要 pre_order_no、外部售后单号和事件 ID 幂等。
24. 售后与退款
flowchart TD
A["创建预订单售后"] --> B["主单 refund_status=退款中"]
B --> C{"售后审核结果"}
C -->|取消| D["恢复无退款,可继续提货"]
C -->|通过| E["等待资金退款"]
E --> F["退款完成回调"]
F --> G{"退款来源"}
G -->|OPS| H["预订单关闭"]
G -->|全部提货自动退款| I["预订单完成"]
G -->|拼团失败| J["拼团失败关闭"]
售后数量归还和资金退款是两套状态,不能只看 refund_status 判断可提数量是否已正确回退。
25. 全部提货完成
任务会扫描 order_status=提货中、refund_status=无退款 且 order_qty=pickedup_qty 的订单,进入 finishPreOrder(),完成剩余定金/差额退款或外部结算,再更新 STATUS_ORDER_FINISH=12。
flowchart TD
A["order_qty = pickedup_qty"] --> B{"refund_status=0 且状态=3"}
B -->|否| X["不处理"]
B -->|是| C["计算已提金额/应退金额"]
C --> D["发起退款或完成处理"]
D --> E{"外部成功"}
E -->|否| F["保持可补偿状态"]
E -->|是| G["状态=12,写 finish_time"]
金额计算要使用拼团最终价、已提数量和定金,不应只按原订单总额减采购单金额猜测。
26. 取消和超时
PreOrderModel::cancelOrder() 使用条件:
sid + pre_order_no + order_status=待支付
因此已支付订单不能走普通取消。用户取消状态 9,任务超时取消状态 10,便于区分责任和退款逻辑。
stateDiagram-v2
ToPay --> UserCancel: 用户主动
ToPay --> AutoCancel: 超时任务
ToPay --> Paid: 支付回调抢先
UserCancel --> [*]
AutoCancel --> [*]
Paid --> ToPick
支付回调与超时取消并发时,支付更新也应带 order_status=1 条件。当前 updateOrderPayed() 仅按 sid + pre_order_no 更新,迟到支付是否可能把已取消单改成待提货,需要重点确认。
27. 新预订单 API
27.1 接口总表
| 方法 | 路径 | 用途 |
|---|---|---|
| POST | /scm/invpre/createpreorder | 创建预订单 |
| POST | /scm/invpre/createpickuporder | 提货并创建采购单 |
| POST | /scm/invpre/getpayresult | 查询支付结果 |
| POST | /scm/invpre/activityquery | 活动查询 |
| POST | /scm/invpre/new_activity_save | 保存预订单活动 |
| POST | /scm/invpre/getpolicylist | 政策/方案列表 |
| POST | /scm/invpre/preorderquerynew | 新预订单查询页面 |
| POST | /scm/invpre/preorderqueryaftersale | 预订单退款查询 |
真实请求字段主要由旧页面表单和 Service 校验决定,联调前从浏览器 Network 固化完整契约。
27.2 提货请求示意
POST /scm/invpre/createpickuporder
Content-Type: application/json
{
"preOrderNo": "PRE-EXAMPLE",
"locationId": 50001,
"entries": [
{"id": 60001, "invId": 30001, "qty": 10}
],
"delayData": {}
}
示例只表达业务字段,不代表 Controller 真实 content-type;旧 CodeIgniter 页面可能使用表单和 JSON 字符串。
28. 首配配置模型
erDiagram
DELIVERY_PLAN ||--o{ SETOUT_TEMPLATE_ITEM : selects
SETOUT_TEMPLATE ||--o{ SETOUT_TEMPLATE_ITEM : belongs_to
SETOUT_TEMPLATE ||--o{ SETOUT_GOODS : has
BS_GOODS ||--o{ SETOUT_GOODS : referenced
| 对象 | 作用 |
|---|---|
| 方案 | 面向站点选择的首配经营方案 |
| 模板/基础包 | 一组商品配置 |
| 方案模板关系 | 一个方案组合多个基础包 |
| 模板商品 | SKU、数量和可能的品牌/供应商规则 |
模板变化只影响后续新建首配单,历史采购订单必须保留下单快照。
29. 首配页面 API
前端 public/js/api/firstMatch.js 调用:
POST /scm/InvPo/firstMatch
页面还会选择方案、仓库、模板和商品数量,最终调用 InvPoService::firstMatch() / confirmDeliveryPlan() 等方法形成订单数据。
30. 首配商品生成
sequenceDiagram
participant UI as 首配页面
participant PO as InvPoService
participant FM as FirstMatchSer
participant Plan as 方案/模板表
participant Goods as 商品缓存/供给
participant Balance as OPS退款账户
UI->>PO: sid + 方案 + 仓库
PO->>Plan: 查询方案和模板商品
PO->>FM: 计算商品、数量和广宣品规则
PO->>Goods: 获取 SKU、最小起订量、价格、供应商
PO->>PO: 分直发/非直发/广宣品
PO->>Balance: 查询退款账户余额
PO-->>UI: 分组明细、总数量、总金额
30.1 三类分组
| 分组 | 代码数据 | 特点 |
|---|---|---|
| 直发 | item['1'] | 还会按供应商拆组 |
| 非直发 | item['2'] | 统一仓配 |
| 广宣品 | item['3'] | 特定规则下赠送/0 元 |
广宣品价格和金额可以为 0,但仍计入数量。支付金额和采购数量不能用同一总数推断。
31. 首配订单限制
普通采购保存时会判断是否属于必须走新首配入口的商品/站点,若不允许普通首配,会提示切换到首配订单页面。
首配订单统一按期货处理。支付、出库和入库仍进入采购主链,但订单类型、批次、0 元处理和退货类型不同。
32. 首配采购状态
首配最终使用采购订单状态机,详见 14_采购完整排查手册.md。典型链路:
stateDiagram-v2
Draft --> Submitted
Submitted --> ToPay: 在线支付
Submitted --> WaitingOut: 无需支付/挂账
ToPay --> WaitingOut: 支付成功
WaitingOut --> Delivery: 部分/全部发货
Delivery --> WaitingIn: 待入库
WaitingIn --> Finished: 入库完成
Submitted --> Closed: 关闭
WaitingOut --> Closed: 未发货关闭
首配 0 元订单无需支付,但状态如何直接推进应以采购 Service 为准。
33. 首配退货
首配退货类型为 30-Cxx-18,页面有独立列表和详情文案。
flowchart TD
A["选择原首配采购入库商品"] --> B["校验可退数量和额度"]
B --> C["生成首配退货申请"]
C --> D["审核/出库"]
D --> E["供应商/OPS处理"]
E --> F["退款和采购数量账更新"]
首配退货额度过期由订单中心事件 ordercenter_aftersale_return_quota_expire 通知,不能仅按普通采购退货判断。
34. 首配与电池站
电池站菜单中会隐藏首配采购。后端仍应防止电池站绕过菜单调用首配接口。详见 42_电池用户_蓄电池可售范围专题.md。
35. 旧预订单兼容
旧表 t_scm_preorder* 仍被 InvPreService、历史报表和核销逻辑使用。新旧体系区别:
| 维度 | 旧预订单 | 新预订单 |
|---|---|---|
| 主表 | t_scm_preorder | t_pre_order |
| 商品 | t_scm_preorder_info | t_pre_order_info |
| 支付/提货 | 独立 pay/spend 表 | 主单累计字段 + 来源采购单 |
| 外部订单 | 历史 NC/核销 | 订单中心 odc_order_no |
| 状态 | 旧 Service 私有映射 | PreOrderEnums |
排查时先根据表和单号确认版本,不能把新状态值套到旧表。
36. 任务和补偿
任务入口 application/controllers/tasks/PreOrder.php 负责或调用:
- 待支付超时取消。
- 拼团成功/失败处理。
- 可提货通知。
- 全部提货后的退款/完成。
- 订单中心/支付状态补偿。
flowchart TD
A["定时扫描"] --> B{"任务类型"}
B -->|待支付超时| C["状态 1 -> 10"]
B -->|拼团结束| D["成功开放提货 / 失败关闭退款"]
B -->|提货窗口| E["发送站内消息/通知"]
B -->|全部提货| F["完成退款和状态 12"]
B -->|外部不一致| G["查询支付/订单中心并补偿"]
每个任务必须有固定扫描边界、幂等键和状态条件,详细治理见第 40 篇。
37. 通知
消息类型 PRE_ORDER_PICK_UP=60,跳转到“预购订单查询(新)”。
通知只提示用户,不改变提货状态。重复通知应去重,漏通知不应阻止用户从订单页面提货。
38. 关键并发窗口
38.1 支付与取消
sequenceDiagram
participant Timeout as 超时任务
participant Pay as 支付回调
participant DB as 预订单
Timeout->>DB: status 1 -> 10 条件更新
Pay->>DB: 更新为 status 2
Note over Timeout,Pay: 支付更新若不带原状态条件,迟到回调可能复活已取消订单
38.2 两次提货
sequenceDiagram
participant A as 提货请求A
participant B as 提货请求B
participant DB as 预订单明细
A->>DB: 读取未提=10
B->>DB: 读取未提=10
A->>DB: 提货10并创建采购单
B->>DB: 提货10并创建采购单
Note over A,B: 无行锁/条件更新时累计提货20,超过订购10
38.3 关闭重复回退
同一采购关闭事件重复消费会重复减 pickedup_qty。需要以来源采购单号+明细+关闭事件做幂等。
39. 完整排查 SOP
flowchart TD
A["拿到预订单号/首配采购单号"] --> B{"属于首配还是新/旧预订单"}
B -->|首配| C["查 orderType=30-Cxx-09、方案和模板快照"]
B -->|新预订单| D["查 t_pre_order 主从"]
B -->|旧预订单| E["查 t_scm_preorder*"]
D --> F["查状态/退款/活动提货窗口"]
F --> G["按来源单号查全部采购单"]
G --> H["汇总有效提货、关闭和售后数量"]
H --> I["校验主从数量守恒"]
I --> J["查订单中心/支付中心状态"]
J --> K["查任务和回调日志"]
C --> L["查采购支付/出库/入库/退货"]
40. 常见问题
| 现象 | 首查 | 可能原因 |
|---|---|---|
| 预订单不能支付 | 状态是否 1 | 已取消、已支付、订单中心状态异常 |
| 支付成功仍待支付 | 支付回调 | 回调漏失、sid/单号不匹配 |
| 不能提货 | canPick 四条件 | 窗口、退款、状态、无剩余量 |
| 可提数量比预期少 | 明细 pickedup_qty | 已生成采购单、未回退关闭量 |
| 可提数量变多 | 关闭回退 | 重复回退或采购单重复关闭事件 |
| 提货超过订购量 | 并发 | 缺行锁/条件原子更新 |
| 全部提货未完成 | 完成任务 | 状态/退款条件不命中、外部退款失败 |
| 拼团价错误 | 阶梯和总参团批数 | 阶梯版本、固定价/折扣、锁价时点 |
| 首配金额为 0 | 是否广宣品/0 元单 | 业务正常或模板价格错误 |
| 首配商品与模板不同 | 历史快照 | 模板已变,不应反改旧单 |
41. 只读 SQL
41.1 新预订单主单
SELECT id, sid, pre_order_no, odc_order_no,
order_status, refund_status,
order_qty, pickedup_qty, picked_num_true,
batch_num, picked_batch_num, pickedup_times,
order_amount, handsel_amount,
source_type, source_id, activity_plan_id,
order_time, pay_time, finish_time, refund_time
FROM t_pre_order
WHERE sid = :sid
AND pre_order_no = :pre_order_no;
41.2 明细守恒
SELECT id, inv_id, sku_id, qty, pickedup_qty,
qty - pickedup_qty AS unpicked_qty,
price, amount, is_gift
FROM t_pre_order_info
WHERE sid = :sid
AND pre_order_no = :pre_order_no
ORDER BY id;
SELECT SUM(qty) AS detail_order_qty,
SUM(pickedup_qty) AS detail_picked_qty,
SUM(qty - pickedup_qty) AS detail_unpicked_qty
FROM t_pre_order_info
WHERE sid = :sid
AND pre_order_no = :pre_order_no;
41.3 来源采购单
SELECT id, sid, billNo, orderType, srcOrderType, srcOrderNo,
billStatus, closeType, paymentType, billDate
FROM <po_order_shard>
WHERE sid = :sid
AND srcOrderType = 12
AND srcOrderNo = :pre_order_no
ORDER BY id;
字段 srcOrderNo 需按当前 DDL 确认,部分实现可能使用其他来源字段。
41.4 活动配置
SELECT *
FROM t_pre_order_activity_settings
WHERE activity_id = :activity_id;
SELECT *
FROM t_pre_activity_group_level
WHERE activity_id = :activity_id
ORDER BY level_step;
41.5 首配方案
SELECT p.*, i.*, t.name AS template_name
FROM t_bs_delivery_plan p
LEFT JOIN t_bs_setout_template_item i ON i.plan_id = p.id
LEFT JOIN t_bs_setout_template t ON t.id = i.template_id
WHERE p.id = :plan_id;
表真实关联字段以生产 DDL 为准。
42. 代码检索
rg -n "STATUS_TO_PAY|STATUS_TO_PICK|STATUS_PICKING|STATUS_ORDER_FINISH" \
application/KzData/Enums/PreOrderEnums.php
rg -n "submitPreOrder|createPoOrder|returnPickedQty|finishPreOrder|closePreOrder" \
application/Services/PreOrders
rg -n "ORDERTYPE_PRE_ORDER|ORDERTYPE_FIRST_ORDER|SRC_ORDER_TYPE_PRE_ORDER" application
rg -n "BS_DELIVERY_PLAN|BS_SETOUT_TEMPLATE|firstMatch|confirmDeliveryPlan" \
application public/js
43. 故障树
flowchart TD
A["首配/预订单异常"] --> B{"是哪类单据"}
B -->|首配| C["方案模板 -> 商品分组 -> 采购单"]
B -->|新预订单| D{"下单/支付/提货/关闭/退款哪一阶段"}
B -->|旧预订单| E["旧表/支付/提货/核销链"]
D -->|下单| F["活动配置/最小批量/订单中心"]
D -->|支付| G["状态1/支付中心/迟到回调"]
D -->|提货| H["窗口/退款/剩余量/并发"]
D -->|关闭| I["采购来源/数量回退/幂等"]
D -->|退款| J["售后状态/资金状态/完成任务"]
44. 已识别风险
| 编号 | 风险 | 影响 |
|---|---|---|
| R43-01 | 新旧预订单并存 | 状态、表和报表口径容易混用 |
| R43-02 | 本地事务内调用订单中心 | 外部成功本地回滚导致孤儿单 |
| R43-03 | 支付成功更新未见原状态条件 | 迟到支付可能复活已取消单 |
| R43-04 | 提货先读剩余量再累计 | 并发可能超提 |
| R43-05 | 明细原子加减无上下界条件 | 可超过 qty 或减成负数 |
| R43-06 | 关闭回退依赖事件幂等 | 重复消息会重复归还额度 |
| R43-07 | pickedup_qty 与 picked_num_true 双口径 | 报表和可提数量可能不一致 |
| R43-08 | STATUS_PICK_FINISH=4 与完成扫描逻辑并存 | 状态 4 是否真实使用不明确 |
| R43-09 | 拼团价可在格式化时覆盖 | 锁价时点和历史价格解释困难 |
| R43-10 | 活动提货状态 3 文案映射不完整 | 页面可能显示未知状态 |
| R43-11 | 订单中心/支付/退款跨系统 | 本地事务不能保证最终一致 |
| R43-12 | 首配模板实时变化 | 若未完整快照,历史单解释困难 |
| R43-13 | 首配 0 元和广宣品有数量无金额 | 金额汇总不能代表商品数量 |
| R43-14 | 电池站只靠菜单隐藏首配 | 接口可能被绕过 |
| R43-15 | 预订单直发不允许直采退货 | 通用退货入口可能误导用户 |
| R43-16 | 通知与提货状态解耦 | 漏通知不能用来判断不可提 |
| R43-17 | 完成依赖周期任务 | 调度失败会长期停在提货中 |
| R43-18 | 来源采购单字段和唯一键待确认 | 无法可靠去重提货和回退 |
45. 回归清单
45.1 预订单创建
- [ ] 普通活动、拼团活动。
- [ ] 按金额定金、按批数定金。
- [ ] 最小提货批量边界。
- [ ] 活动未开始、可下单、已结束提货。
- [ ] 普通商品、赠品、套餐。
- [ ] 订单中心成功/超时/重复提交。
45.2 支付
- [ ] 待支付正常支付。
- [ ] 重复支付回调。
- [ ] 用户取消与支付并发。
- [ ] 超时取消与迟到支付。
- [ ] 预售专属授信。
- [ ] 支付查询和补偿。
45.3 提货
- [ ] 未开始、预提货、已开始、已结束。
- [ ] 一次全部提货、多次部分提货。
- [ ] 按件数/按金额最小限制。
- [ ] 最大提货次数和省份限制。
- [ ] 两请求并发提同一商品。
- [ ] 赠品、最小起订量和箱规。
- [ ] 创建采购单失败时数量不增加。
- [ ] 数量增加后订单中心同步失败补偿。
45.4 关闭和售后
- [ ] 部分关闭归还数量。
- [ ] 整次提货关闭归还次数/批数。
- [ ] 重复关闭消息。
- [ ] 售后创建、取消、审核、退款完成。
- [ ] OPS 退款、全部提货自动退款、拼团失败。
- [ ] 全部提货完成任务。
45.5 首配
- [ ] 单模板、多模板方案。
- [ ] 直发、非直发、广宣品。
- [ ] 0 元商品和退款账户余额。
- [ ] 仓库未选、商品失效、最小起订量。
- [ ] 普通采购绕过首配拦截。
- [ ] 电池站接口拦截。
- [ ] 首配支付、出库、入库和首配退货。
46. 改动影响面
| 改动 | 最小回归 |
|---|---|
PreOrderEnums | 页面、任务、支付、提货、售后、订单中心回调 |
submitPreOrder | 活动下单、金额、订单中心幂等 |
createPoOrder* | 预订单、采购、库存、支付、数量回写 |
returnPickedQty* | 采购关闭、售后、重复事件、可提数量 |
finishPreOrder | 全提、退款、资金和状态完成 |
| 活动阶梯 | 拼团统计、锁价、提货价和退款 |
| 首配方案 | 页面、直发拆单、广宣品、采购和退货 |
47. 生产待确认项
- 新旧预订单当前流量比例和历史数据截止时间。
t_pre_order.pre_order_no、odc_order_no的唯一索引。- 预订单到采购单的正式来源字段及唯一约束。
- 订单中心提交和提货同步的幂等键、重试与查询接口。
- 支付回调对已取消/已关闭预订单的迟到处理。
- 提货行锁或条件原子更新的生产实现。
picked_num_true与pickedup_qty的业务差异。- 状态 4“全部提货”是否仍实际使用。
- 拼团最终价锁定时点和阶梯版本快照。
- 提货状态 3“预提货”的页面和业务规则。
- 完成、超时、通知任务的真实调度频率和告警。
- 预订单退款资金来源、金额公式和支付中心回调。
- 首配方案/模板管理入口和历史快照字段。
- 首配采购后端对电池站的强制校验。
- 旧预订单报表和新预订单报表是否已经统一口径。
48. 证据索引
| 主题 | 代码路径 |
|---|---|
| 预订单状态 | application/KzData/Enums/PreOrderEnums.php |
| 活动配置状态 | application/KzData/Enums/PreOrderSettingEnums.php |
| 采购类型 | application/KzData/Enums/PoOrderEnums.php |
| Controller | application/controllers/scm/InvPre.php |
| 页面编排 | application/service/scm/InvPreService.php |
| 新预订单核心 | application/Services/PreOrders/PreOrderService.php |
| 提货校验 | application/Services/PreOrders/PreOrderCheckService.php |
| 统计 | application/Services/PreOrders/PreOrderStatisticService.php |
| 外部回调 | application/Services/PreOrders/PreOrderNotifyService.php |
| 定时任务 | application/controllers/tasks/PreOrder.php |
| 主表 Model | application/models/orders/PreOrderModel.php |
| 明细 Model | application/models/orders/PreOrderInfoModel.php |
| 首配采购 | application/service/scm/InvPoService.php |
| 首配页面 | public/js/page/firstMatch.js |
49. 最终理解
flowchart LR
A["活动/方案快照准确"] --> F["业务正确"]
B["支付状态可靠"] --> F
C["提货数量原子守恒"] --> F
D["采购关闭可幂等回退"] --> F
E["跨系统退款最终一致"] --> F
首配是“一次性形成首批采购”,预订单是“先获得未来提货权,再多次转采购”。整个专题最核心的正确性不是单据状态,而是额度守恒:任何一件商品都只能处于未提、有效采购履约、已回退或售后退款中的一个位置;支付、订单中心和任务失败不能让它被重复提货,也不能让合法额度永久丢失。
请求-日志-数据变更追踪卡
多入口请求链路
| 场景 | 调用方与入口 | 请求载荷/上下文 | Controller/Consumer | Service/Provider | 汇合点 | 最终业务事实 |
|---|---|---|---|---|---|---|
| 创建预订单 | PC scm/InvPre | sid、商品、总额度、有效期、配置 | InvPre Controller | InvPreService/PreOrderService | preorder bill | 预订单主明细和可提额度 |
| 提货转采购 | PC/任务 | preorder、SKU、提货量、收货信息 | InvPre/PreOrder task | CheckService + InvPoService | preorder + PO bill | 锁定额度并生成采购单 |
| 首配 | /moveMall/FirstMatch | 站点、首配方案、商品数量 | FirstMatch Controller | FirstMatchSer | first-match ID + PO | 一次性形成首批采购 |
| 回调/回退 | 支付/订单中心/PreOrder task | PO 支付/关闭/退款状态 | Pay/Order/PreOrder Consumer | Notify/Statistic Service | PO + preorder relation | 完成提货或回退额度 |
日志证据矩阵
| 链路段 | 日志来源 | 可检索锚点 | 成功信号 | 失败信号 | 与下一段关联方式 | | --- | --- | --- | --- | --- | --- | --- | | 预订单 | InvPre/PreOrderService | request_id、preorder no、sid、SKU | 主明细额度 commit | 重复、配置/有效期失败 | preorder ID 进入提货 | | 转采购 | Check/InvPo Service | preorder、PO billNo、SKU、qty | 锁额度和 PO 关系完整 | 额度已扣但 PO 创建失败、重复提货 | relation 串联两单 | | 支付履约 | Pay/Order Consumer | pay/message ID、PO/preorder | 有效 PO 转已用额度 | 回调乱序、关闭后支付 | PO 关系查额度状态 | | 回退/任务 | PreOrder task | task batch、PO、SKU、return qty | 仅无效履约额度回退且重跑 0 变化 | 重复回退、漏回退 | batch+relation 数量对账 |
环节数据变更台账
| 步骤 | 代码位置 | 事务 | 读取事实 | 写入表/缓存/MQ | 字段或数量变化 | 回查证据 |
|---|---|---|---|---|---|---|
| 建预订单 | PreOrderService | 预订单事务 | 配置、商品、总额度 | SCM_PRE_ORDER(_INFO) | total=n;available=n;used/frozen=0;status 初始化 | preorder+SKU 额度 |
| 提货锁额 | PreOrderCheckService | 锁额事务 | available、有效期、幂等关系 | 预订单明细/提货关系 | available -n、frozen +n | preorder+request key |
| 生成采购 | InvPoService | 跨预订/采购边界需验证 | frozen 额度、商品地址 | SCM_PO_ORDER*、关系 | PO insert;order type/source 写明 | preorder+PO+SKU |
| 支付/履约 | Notify/Statistic Service | 回调事务 | PO 最终有效状态 | 预订额度统计 | frozen -n、used +n;状态按总额推进 | pay/message ID、额度守恒 |
| 关闭/售后回退 | PreOrderNotify/Task | 幂等事务 | PO 关闭/退款最终量、已回退量 | 额度/关系/统计 | frozen/used -n、available +n,不超过原提货量 | relation、回退流水、重跑 0 变化 |
子模块追踪:preorder-create 预订单创建与定金
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 创建预订 | PC 创建预订单/商品额度 | preorderNo、sid、SKU、total、deposit | application/controllers/scm/InvPre.php -> application/Services/PreOrders/PreOrderService.php | 配置、商品、总额度、有效期和来源唯一性 | 预订单本地事务 insert 主明细,total/available=n、used/frozen=0、状态初始化 | request ID + preorder + SKU + amounts | 配置/重复/金额非法整单回滚;超时按 preorderNo 回查 |
| 定金关系 | 预订需定金时生成支付关系 | preorderNo、deposit payNo | application/service/scm/InvPreService.php | 应付定金、已有支付和订单状态 | 本地资金关系 none -> pending;外部支付事务外 | request ID + preorder/payNo + deposit | 外部超时先查支付终态,禁止重复定金单 |
子模块追踪:preorder-pay 预订单支付
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 支付回调 | 预订单定金/货款成功 | message ID、payOrderNo、preorderNo | application/Services/PreOrders/PreOrderNotifyService.php | 外部成功、已有支付、预订单允许态和金额 | 回调本地事务 payment pending -> confirmed、订单状态单向推进 | message ID + pay/preorder + amount | 重复 0 增量;预订单账户方向不套普通采购 |
| 全景回查 | 支付成功预订单不动 | preorder/pay IDs、payment rows | application/Services/PreOrders/PreOrderStatisticService.php | 支付明细、订单态、额度统计和关联采购 | 查询只读;金额/状态/额度闭环 | pay/preorder + ledger/status | 外部成功只补本地回调,不直接改状态无支付流水 |
子模块追踪:preorder-pick-check 可提货与最小提货校验
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 提货校验 | 用户选择预订单商品提货 | preorder/entry、request key、qty、time | application/Services/PreOrders/PreOrderCheckService.php | paid/valid、available、最小提货量、倍数和已有关系 | 校验查询只读 不写;不满足时额度/采购 0 变化 | request ID + preorder/entry + qty + rule | 页面可提不等于提交可提;提交锁内重新校验 |
| 多行汇总 | 一次提多个 SKU | preorder、item list、total request | application/Services/PreOrders/PreOrderCheckService.php | 每行 available/min、订单总限制和重复 key | 本地锁额事务前形成标准 item set | request ID + preorder + per-SKU result | 任一行失败整单不锁额,避免部分提货 |
子模块追踪:preorder-pick-po 提货转采购
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 锁额建单 | 提货确认生成采购 | preorder/request key、SKU、qty、address | application/controllers/scm/InvPo.php -> application/service/scm/InvPoService.php | frozen 额度、商品地址、采购类型/来源和已有关系 | 跨预订/采购本地事务边界:PO insert、预订关系 insert | request ID + preorder/po + SKU/qty | PO 创建失败释放本次 frozen;来源键防重复采购 |
| 三方回查 | 预订单额度扣了无采购 | preorder/request/po IDs | application/models/orders/PreOrderInfoModel.php | 锁额流水、关系、采购主明细 | 查询只读;三方数量相同 | all IDs + relation/row counts | 已有 PO 只补关系/响应,不再建单 |
子模块追踪:preorder-quota 提货额度原子更新
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 原子锁额 | 并发提货提交 | preorder/entry、request key、qty | application/Services/PreOrders/PreOrderCheckService.php | available、frozen、used、有效期和版本 | 条件更新本地事务 available -n, frozen +n,写唯一提货关系 | request ID + preorder/request + before/after + rows | affected rows=0 视为额度不足/并发冲突,不继续建 PO |
| 守恒核对 | 额度负数/不平 | preorder/entry、all relations | application/Services/PreOrders/PreOrderStatisticService.php | total、available、frozen、used、returned | 查询只读;total=available+frozen+used 按返还口径调整 | preorder + relation IDs + totals | 禁止直改汇总;按原 request/PO 做幂等补偿 |
子模块追踪:preorder-close-return 采购关闭与额度回退
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 关闭回调 | 关联采购取消/关闭 | message ID、po/preorder relation、close qty | application/Services/PreOrders/PreOrderNotifyService.php | PO 最终关闭、未履约量、frozen/used 和已回退 | 单回调本地事务 frozen/used -n, available +n,不超过提货量 | message ID + po/preorder + return qty | 支付/履约不确定不回退;重复 0 增量 |
| 回退回查 | PO 关闭但 available 未恢复 | po/preorder、return ledger | application/Services/PreOrders/PreOrderStatisticService.php | 采购关闭明细、额度流水和统计 | 查询只读;关系级 picked=used/frozen+returned | both order IDs + ledger | 只补缺失回退流水/统计,不重复关闭采购 |
子模块追踪:preorder-aftersale 预订单售后与退款
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 售后申请 | 预订单取消/退款 | preorderNo、aftersale/refundNo、amount | application/Services/PreOrders/PreOrderService.php | 已支付/已提货、可退金额、关联采购和已有售后 | 售后本地事务 none -> pending,写原支付/预订关系 | request ID + preorder/aftersale/refund | 已转采购部分按责任边界处理,避免双退 |
| 退款回调 | 支付中心退款结果 | message ID、original/refund payNo | application/Services/PreOrders/PreOrderNotifyService.php | 外部终态、已退额和预订单状态 | 回调本地事务 refunded +n、售后 pending -> complete | message ID + original/refund/preorder | 重复 0 增量;额度与资金分别验收 |
子模块追踪:firstmatch-config 首配方案配置与商品生成
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 配置方案 | 运营/站端维护首配规则 | plan ID、sid、rules/products | application/controllers/moveMall/FirstMatch.php -> application/Services/FirstMatch/FirstMatchSer.php | 站点、方案版本、商品和冲突规则 | 配置本地事务 plan/version/status old -> new、全量关系替换 | request ID + plan/sid + product count | 使用中关键规则按合同限制;失败整批回滚 |
| 生成商品 | 按方案/站点生成推荐首配商品 | plan/sid、generation batch | application/Services/FirstMatch/FirstMatchSer.php | 当前方案、商品可用性、库存/价格和历史生成 | 生成本地事务/派生表 old set -> generated set | batch + plan/sid + candidates/results | 只生成符合规则商品;重跑稳定键不重复关系 |
子模块追踪:firstmatch-po 首配采购单生成与履约
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 生成采购 | 站点确认首配方案 | plan/generation ID、sourceOrderNo、items | application/Services/FirstMatch/FirstMatchSer.php -> application/service/scm/InvPoService.php | 生成商品、价格、供应商、地址和来源唯一性 | 采购本地事务 insert PO/关系,写首配 orderType/source | request ID + plan/source/po + SKU qty | 多供应商部分失败按已建关系回滚/关闭;来源键幂等 |
| 履约统计 | 支付/发货/入库回调 | po/plan IDs、message ID | application/Services/PreOrders/PreOrderNotifyService.php | PO 支付/发收/关闭和首配关系 | 单回调本地事务更新首配统计 old -> recomputed | message + po/plan + qty/status | 采购事实为准;统计缺失只补统计/通知 |
子模块追踪:firstmatch-return 首配退货与补偿任务
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 首配退货 | 原首配采购发起退货 | plan/po/return billNos、SKU、qty | application/service/scm/InvPoService.php | 原入库、已退、首配来源和库存 | 退货与库存本地事务 returnQty +n、库存 qty -n | request ID + three billNos + transType | 超退/重复零写入;不按普通来源丢失首配关系 |
| 补偿任务 | 扫描关系/统计/回退差异 | task batch、plan/po IDs | application/controllers/tasks/PreOrder.php | PO 最终态、额度/统计/退货和已有补偿 | 每关系本地事务只补 old -> expected,重跑 0 变化 | task + batch + IDs + before/after | 小批、前置事实不符 skip;不重做库存/支付成功段 |