1. 业务目标与边界
采购域负责服务站从供应商、云仓、活动、预订单或订单中心获得商品,并把“采购意向”推进为可支付、可发货、可签收、可入库的业务单据。反向链路包含未发货关闭、采购退货、退货出库、退款和仓库确认。
本篇覆盖:
- PC 老采购入口、新采购/购物车入口和 App 采购入口。
- 草稿保存、修改、提交、审核、在线支付、发货、配送、入库、完成。
- 整单/明细关闭、待支付取消、退货申请和退货出库。
- 订单中心、支付中心、仓储库存和特殊活动来源联动。
不在本篇展开:采购报表每个指标的 DWD 口径、所有金融通道内部字段、每一种特殊订单的独立 PRD。这些在报表、财务、首配、活动专题中继续细分。
2. 参与角色和前置条件
| 角色/系统 | 主要动作 | 前置条件 |
|---|---|---|
| 服务站采购员 | 选品、保存草稿、提交、支付、关闭、退货 | 登录上下文有 JXCSID/JXCUID/JXCUNAME;有采购权限 |
| 供应商/云仓 | 审核、出库、配送、处理退货 | 商品、供应商、仓库和配送规则有效 |
| 订单中心 | 审核采购/售后、取消、关闭、自制单同步 | DGJ2 来源单与订单中心单号能关联 |
| 支付中心 | 支付、退款、异常回调 | 在线支付采购单已创建支付关系 |
| 服务站仓库/PDA | 签收、扫码、确认入库、退货出库 | 普通仓和货位有效;数量未超过可操作数量 |
| 库存域 | 写采购入库/退货出库流水和实时库存 | 单据、明细、交易类型和仓库货位完整 |
| 活动域 | 生成赠品/秒杀特殊采购单,接收支付和关闭结果 | orderType/srcOrderType/srcOrderId/srcOrderNo 正确 |
3. 入口和代码地图
| 入口类型 | 文件/配置 | 主要职责 |
|---|---|---|
| PC 老采购 | application/controllers/scm/InvPo.php | 草稿、提交、支付、关闭、退货、入/出库 |
| 新采购 | application/controllers/po/PurchaseOrder.php | 新版采购接口和远程采购服务适配 |
| 采购购物车 | application/controllers/po/Cart.php | 选品暂存和提交前商品集合 |
| 新售后 | application/controllers/po/AfterSale.php | 退货创建、提交、出库、物流、确认 |
| 采购入库 | application/controllers/scm/InvPu.php | 入库单列表、新增、更新、删除、导出 |
| App API 映射 | application/config/appapis.php | apppur*、appwms* code 到实现映射 |
| 采购核心 Service | application/Services/PoOrders/PoOrderSer.php | 校验、关闭、待支付取消、来源联动 |
| 老采购 Service | application/service/scm/InvPoService.php | 列表详情、关闭、退货、入/出库、支付参数 |
| 保存实现 | application/Services/PoOrders/OrderService/NormalOrderSaveService.php | 普通采购草稿保存 |
| 查询实现 | PoOrderQuerySer.php、PoOrderInfoQuerySer.php | 主单和明细查询 |
| 订单中心消费者 | application/controllers/tasks/OrderCenterNotify.php | 审核、售后、取消、关闭、自制单消息 |
| 支付消费者 | application/controllers/tasks/PayCenterNotify.php | 支付/退款/异常落账和状态推进 |
| 库存 Service | application/Services/Storage/InventorySer.php | 库存流水和实时库存变化 |
4. 采购接口总表
4.1 PC 主链路
| 接口方法 | 核心请求 | Controller 后续调用 | 关键结果 | 数据副作用 |
|---|---|---|---|---|
saveInvPo | postData JSON | 校验 -> PoOrderSer::checkOrderGoods -> NormalOrderSaveService::savePoOrder | 草稿 id | 新增采购主单/明细/扩展 |
updateInvPo | postData JSON,内含 id | 查草稿 -> 重校验商品 -> 保存 | 原草稿 id | 更新草稿和明细 |
submitInvPo | orderId、postData | $this->InvPoService->submitInvPo | 提交结果 | 草稿进入后续状态、可能创建外部单 |
deleteInvPo | id | InvPoService::deleteInvPo | 成功/失败 | 仅允许可删除状态,通常软删 |
cancelWaitPayOrder | id | Redis 锁 -> PoOrderSer::cancelWaitPayOrder | 取消成功 | 待支付转过期/关闭并触发来源释放 |
cancelInvPo | 页面请求数据 | InvPoService::cancelInvPo | 状态和消息 | 取消适用状态的采购单 |
closeInvPo | token 和明细关闭数据 | 校验 -> Redis 锁 -> InvPoService::closeInvPo | 关闭结果 | 写关闭明细,更新数量/关闭状态 |
closeInvPoOrder | token、id | 校验 -> Redis 锁 -> InvPoService::closeInvPoOrder | 整单关闭结果 | 写关闭明细、主单关闭或部分关闭 |
getPayInfo/getPayInfoNew | 采购单/支付上下文 | InvPoService 同名方法 | 支付方式、金额、支付参数 | 通常只读/创建支付前置参数 |
noPagePay | 采购单、支付方式 | InvPoService::noPagePay | 支付受理结果 | 调支付中心,后续靠回调落账 |
exclusivePay | 专属授信支付上下文 | InvPoService::exclusivePay | 空表示成功或错误字符串 | 授信/额度联动 |
unionPayCode | 采购单和支付方式 | InvPoService::unionPayCode | 二维码信息 | 发起银联支付 |
getPayResult | 采购单/支付单标识 | InvPoService::getPayResult | 当前支付结果 | 只读 |
当前分支证据缺口:Controller 明确调用 InvPoService->submitInvPo($data,$orderId),但在当前工作区没有检索到这个方法定义。发布/联调前应确认是否由未同步代码、扩展装载或历史分支提供。本文不虚构其内部实现,提交后的状态以枚举、回调和其他可见 Service 为依据。
4.2 退货和仓内操作
| 接口方法 | 核心请求 | 业务分支 | 数据副作用 |
|---|---|---|---|
saveOrderBack | postData、postDataGoods | 不良品退货;按服务站白名单切新旧采购服务 | 保存退货草稿 |
saveOrderBack_unsale | postData | 滞销品退货;新服务走 RefundProvider | 保存退货草稿 |
submitOrderBack | orderId | 先限制秒杀来源退货,再提交新/旧售后 | 退货单进入审核/处理 |
submitOrderActivityBack | postData | 套餐/活动商品退货 | 创建或提交活动退货 |
cancelReturnInvPo | id | 新服务 RefundProvider::cancel 或老 Service | 取消退货 |
invPoOutStorage | orderId、postData | 退货商品选择仓库/货位出库 | 减库存、写退货出库事实 |
invPoInStroage | goodsList 等 | Redis 锁防重复入库 | 增库存、更新采购已入库量 |
4.3 App 和 WMS code
| code | 映射 | 业务用途 |
|---|---|---|
apppurOrderAddDraft | app.purchase.purOrderAddDraft | App 新增采购草稿 |
apppurOrderAdd | app.purchase.purOrderAdd | App 正式提交采购 |
apppurOrderList | app.purchase.purOrderList | 采购列表 |
apppurOrderListDetail | app.purchase.purOrderlistDetail | 采购详情 |
apppurCloseOrder | app.purchase.purCloseOrder | 关闭采购商品 |
apppurOrderReturnGetGoods | app.purchase.purOrderReturnGetGoods | 获取可退商品 |
apppurOrderReturnAdd | app.purchase.purOrderReturnAdd | 提交采购退货 |
apppurOrderCheckMoq | app.purchase.purOrderCheckMoq | 起订组校验 |
appwmsSignIn | app.wms.sign.signIn | 物流签收 |
appwmsInStoSkuInfo | app.wms.insto.inStoSkuInfo | 扫码识别待入库商品 |
appwmsInStoSkuDetail | app.wms.insto.inStoSkuDetail | 待入库商品详情 |
appwmsInStoInStorage | app.wms.insto.inStoInStorage | 批量采购入库和发货差异 |
appwmsCodeVerify | app.wms.codeVerify | 校验扫描码类型 |
code 是应用协议入口,不等同固定 URL。联调时还要确认 API 网关请求结构、签名和统一返回包装。
5. 保存采购草稿请求
PC 老入口把业务 JSON 放在 postData 字符串中,登录上下文不应由业务表单伪造。
{
"postData": "{\"locationId\":101,\"orderType\":\"30-Cxx-01\",\"description\":\"门店补货\",\"strict\":1,\"entries\":[{\"invId\":10001,\"qty\":10,\"packSpecType\":1}]}"
}
服务端处理顺序:
- 检查
PU_ADD权限。 - 解析
postData,InvPoValidate::saveInvPo校验仓库、备注和订单类型。 - 从会话注入
sid/uid/name。 - 校验仓库存在且不是移动仓,采购只能入普通仓。
- 加载服务站区域,
PoOrderSer::checkOrderGoods校验物料、数量、供应关系和严格模式。 - 急件配送到客户时校验
dispatchType/contactId和联系人地址。 NormalOrderSaveService::savePoOrder保存草稿。- 保存服务站最后一次商品包装规格,返回草稿 ID。
核心字段:
| 字段 | 来源 | 规则 |
|---|---|---|
locationId | 表单 | 必填,必须属于可用普通仓 |
orderType | 表单/来源业务 | 必填,决定校验、支付、履约和退货规则 |
description | 表单 | 最长 50 字;草稿保存会追加草稿说明 |
entries | 表单 | 商品明细;具体字段由 checkOrderGoods 归一 |
strict | 表单 | 默认 1,控制商品校验严格程度 |
dispatchType | 急件表单 | 急件时必传,配送到站或维修客户 |
contactId | 急件客户配送 | 配送到客户时必传并校验地址 |
sid/uid/name | 登录上下文 | 服务端注入,不信任业务 JSON |
修改草稿多传 id,并强制当前 billStatus=0;已提交订单不能通过更新草稿接口改回去。
6. 正向采购主流程
flowchart TD
A[服务站选品] --> B[保存采购草稿]
B --> C[校验权限 仓库 订单类型 商品 数量 供应关系]
C --> D{校验通过}
D -- 否 --> X[返回错误 不生成有效草稿]
D -- 是 --> E[写采购主单 明细 扩展]
E --> F[提交采购单]
F --> G{支付类型/来源规则}
G -- 在线支付 --> H[采购单待支付]
G -- 线下挂账或无需支付 --> I[待处理/审核]
H --> J[支付中心处理]
J --> K{支付结果}
K -- 成功 --> I
K -- 超时/取消 --> L[已过期或关闭 释放来源占用]
I --> M[订单中心/供应方审核]
M --> N{审核结果}
N -- 驳回 --> O[审核不通过]
N -- 通过 --> P[待出库]
P --> Q[供应方出库]
Q --> R[配送中]
R --> S[服务站签收/入库]
S --> T[库存增加 已入库数量累加]
T --> U{全部履约或仍有未完成量}
U -- 全部完成 --> V[采购单已完成]
U -- 部分 --> W[保留待入库/配送或处理差异关闭]
7. 系统调用时序
sequenceDiagram
participant U as 服务站用户
participant C as InvPo Controller
participant PS as 采购 Service
participant DB as 采购数据库
participant OC as 订单中心
participant PAY as 支付中心
participant WMS as 仓储/PDA
participant INV as InventorySer
U->>C: saveInvPo(postData)
C->>PS: checkOrderGoods + savePoOrder
PS->>DB: INSERT po_order/po_order_info
DB-->>U: draft orderId
U->>C: submitInvPo(orderId,postData)
C->>PS: 提交采购
alt 在线支付
PS->>DB: billStatus=待支付
PS->>PAY: 发起支付
PAY-->>C: PayCenterNotify 支付回调
C->>DB: payment/payment_info/payinfo + 采购状态
else 线下或无需支付
PS->>DB: billStatus=待处理/审核
end
PS->>OC: 同步正向采购
OC-->>C: OrderCenterNotify 审核/取消/关闭消息
C->>DB: 更新审核和履约状态
OC-->>WMS: 发货/物流
WMS->>C: 签收/入库请求
C->>INV: 保存库存流水并增加实时库存
INV->>DB: inventory/inventory_info/real_time
C->>DB: 更新采购明细已入库数量和主单完成状态
8. 采购状态机
状态定义在 application/KzData/Enums/PoOrderEnums.php:
| 值 | 常量 | 业务含义 | 典型可执行动作 |
|---|---|---|---|
| 0 | BILLSTATUS_DRAFT | 草稿 | 修改、删除、提交 |
| 1 | BILLSTATUS_SUBMITED | 待处理 | 等待审核/受理,按规则可取消 |
| 2 | BILLSTATUS_AUDITING | 审核中 | 等订单中心/OA 结果 |
| 3 | BILLSTATUS_AUDIT_PASS | 待出库 | 仓库/供应方出库,未发货量可按规则关闭 |
| 4 | BILLSTATUS_DELIVERY | 配送中 | 签收、入库、差异处理 |
| 5 | BILLSTATUS_FINISHED | 已完成 | 查询、售后;正向不可再编辑 |
| 8 | BILLSTATUS_AUDIT_UNPASS | 审核不通过 | 查看原因,部分业务可复制重建 |
| 9 | BILLSTATUS_CLOSED | 已关闭 | 终态;核对关闭和退款 |
| 10 | BILLSTATUS_WAIT_PAY | 待支付 | 支付、取消、超时关闭 |
| 11 | BILLSTATUS_WAIT_REFUND | 待退款 | 等支付中心退款结果 |
| 12 | BILLSTATUS_PAY_OVERTIME | 已过期 | 终止待支付,释放来源资源 |
| 13 | BILLSTATUS_WAIT_STORAGE_IN | 退货待仓库入库 | 仓库确认退货收货 |
stateDiagram-v2
[*] --> 草稿: saveInvPo
草稿 --> 待支付: 提交且在线支付
草稿 --> 待处理: 提交且线下/无需支付
待支付 --> 待处理: 支付成功
待支付 --> 已过期: 超时或待支付取消
待支付 --> 待退款: 支付异常/退回场景
待处理 --> 审核中: 外部受理
审核中 --> 待出库: 审核通过
审核中 --> 审核不通过: 审核拒绝
待出库 --> 配送中: 出库/发货
配送中 --> 已完成: 全部签收并入库
待处理 --> 已关闭: 允许取消/关闭
待出库 --> 已关闭: 未发货关闭满足条件
已过期 --> [*]
审核不通过 --> [*]
已关闭 --> [*]
已完成 --> [*]
这是一张主流程图,不表示所有订单类型都能走每条边。特殊活动、直发、自提、首配、退货、授信订单会额外限制动作。
9. 订单类型和来源类型
orderType 决定“这是什么业务单”;srcOrderType/srcOrderId/srcOrderNo 决定“它从哪里来”。两者不能混用。
orderType | 名称 | 特殊点 |
|---|---|---|
30-Cxx-01 | 普通采购 | 基础正向流程 |
30-Cxx-07 | 急件到付 | 需要配送类型,可能直接送客户 |
30-Cxx-70 | 急件自提 | 自提履约路径 |
30-Cxx-08 | 预订采购 | 与预订单来源关联 |
30-Cxx-05 | 滞销品退货 | 反向出库/审核 |
30-Cxx-06 | 不良品退货 | 可能关联理赔/原采购明细 |
30-Cxx-16 | 直采退货 | 直采售后规则 |
30-Cxx-17 | 未发货退货 | 关闭/退款联动更强 |
30-Cxx-18 | 首配退货 | 首配来源限制 |
30-Cxx-75 | 赠品活动订单 | 活动生成,普通关闭受限 |
30-Cxx-77 | 秒杀采购订单 | 来源类型 13,支付/关闭回调活动域 |
关键来源值:0 普通、8 采购退货、10 在线支付未发货退款、12 预订单、13 秒杀活动。
10. 支付数据链路
flowchart LR
A[采购单待支付] --> B[获取支付信息/二维码/无页面支付]
B --> C[支付中心受理]
C --> D[PayCenterNotify 消费支付结果]
D --> E{payStatus 是否成功口径}
E -- 否 --> F[异常/退款分支 记录日志并按事件处理]
E -- 是 --> G[写 t_scm_payment]
G --> H[写 t_scm_payment_info]
H --> I[写 t_scm_po_order_payinfo]
I --> J[采购单退出待支付并记录 paymentTime]
J --> K{特殊来源}
K -- 秒杀 --> L[locked_qty 转 used_qty]
K -- 预订单/活动/授信 --> M[调用各来源后处理]
K -- 普通 --> N[进入审核/待处理链路]
支付成功并不只改采购状态。当前回调还可能:
- 生成付款主单和明细。
- 更新账户/授信/专属活动额度。
- 写采购支付关联表。
- 推进预订单或秒杀来源状态。
- 触发任务记录和后续审核/工单逻辑。
所以支付问题不能通过手工把 billStatus 改成 1 解决;必须核对账务、来源单和重复回调幂等。
11. 采购关闭流程
11.1 关闭类型
| 常量 | 值 | 含义 |
|---|---|---|
CLOSETYPE_NONE | 0 | 未关闭 |
CLOSETYPE_PART | 1 | 部分明细关闭 |
CLOSETYPE_ALL | 2 | 整单关闭 |
11.2 主流程
flowchart TD
A[用户/订单中心/超时任务发起关闭] --> B[校验服务站 单据 状态 token]
B --> C[Redis 锁防重复]
C --> D{待支付取消?}
D -- 是 --> E[PoOrderSer cancelWaitPayOrder]
D -- 否 --> F[校验明细未发货/可关闭量和特殊订单限制]
F --> G{可关闭}
G -- 否 --> X[返回 can_not_close_reason]
G -- 是 --> H[写 po_close_info]
H --> I[更新明细数量拆分和主单 closeStatus/billStatus]
I --> J[向订单中心/下游发送关闭确认或取消]
J --> K{来源为秒杀/赠品/预订单}
K -- 是 --> L[来源域释放锁量/额度/状态]
K -- 否 --> M[完成关闭]
关闭必须区分“未发货关闭”和“已出库后的退货”。已出库数量不能伪装成关闭数量,否则采购、库存和财务三套事实会不一致。
12. 采购退货流程
sequenceDiagram
participant U as 服务站
participant C as InvPo/AfterSale
participant S as 退货 Service/Provider
participant DB as 采购退货表
participant OC as 订单中心
participant W as 仓库
participant PAY as 支付中心
U->>C: 选择原采购明细和可退数量
C->>S: saveOrderBack / create
S->>DB: 保存退货草稿和原单关联
U->>C: submitOrderBack(orderId)
C->>S: 校验来源和可退数量
S->>OC: 提交售后审核
OC-->>C: aftersale audit/update callback
alt 审核通过需服务站出库
U->>C: invPoOutStorage(locationId,areaId)
C->>DB: 写退货出库事实并减少库存
W-->>C: confirm warehouse/close
else 未发货退货
OC-->>C: 直接关闭未发货量
end
PAY-->>C: refund result
C->>DB: 更新退款/退货状态和付款关联
退货保存有新旧双路径:PoPrivilegeAuthSer::isCallNewPoServer(sid) 为真时走 RefundProvider,否则走 DGJ2 本地 Service。排查同一接口在不同服务站表现不同时,第一步先确认这个路由开关。
秒杀来源退货还会经过 assertFlashActivityReturnAllowed... 限制,不能绕过活动生命周期直接做普通滞销品退货。
13. 采购入库与库存变化
13.1 仓内流程
flowchart TD
A[物流签收/待入库列表] --> B[扫描箱码/商品码]
B --> C[定位采购单 明细 仓库 货位]
C --> D[计算发货量 已入库量 差异量 可入库量]
D --> E{数量和货位合法}
E -- 否 --> X[拒绝并返回超量/货位/商品错误]
E -- 是 --> F[Redis 锁 goodsList 防重复提交]
F --> G[写采购入库单和明细]
G --> H[InventorySer 写库存流水]
H --> I[实时库存和货位库存增加]
I --> J[更新采购明细已入库数量]
J --> K{全部完成}
K -- 是 --> L[采购单已完成]
K -- 否 --> M[保留配送/待入库并记录差异]
InvPo::invPoInStroage 使用 sid + md5(goodsList) 的 Redis 锁,锁时长 30 秒。它只能降低重复点击风险,最终仍需依靠数据库数量校验和事务保证不超入。
13.2 数量事实
采购数量排查至少同时看:
- 采购明细原始数量。
- 数量拆分表中的发货、入库、关闭、退货等累计值。
- 采购出库/物流明细事实。
- 采购入库单和库存流水。
- 关闭明细。
- 报表同步数据及延迟。
不能仅通过主单状态判断“是否已经入库”,也不能只修采购表而不核对库存流水和实时库存。
14. 外部消息入口
OrderCenterNotify 在订单中心 destination 上注册以下采购相关事件:
| 事件类别 | 消费方法 | 主要作用 |
|---|---|---|
| 售后审核 | orderCenterAfterSaleAudit | 更新采购退货审核状态 |
| 售后关闭 | orderCenterAfterSaleClose | 关闭售后并联动数量/退款 |
| 售后更新 | orderCenterAfterSaleUpdate | 同步售后字段和进度 |
| 仓库确认 | orderCenterAfterSaleConfirmWareHouse | 推进退货仓库确认 |
| 正向采购审核 | orderCenterOrderAudit | 审核通过/驳回映射采购状态 |
| 订单取消 | orderCenterOrderCancel | 同步取消和来源释放 |
| 售后退款完成 | 对应 refund finish callback | 更新退货退款结果 |
| 自制订单同步/关闭 | orderCenterSelfOrderNotify、orderCenterSelfOrderCloseNotify | 赠品等自制采购单同步 |
支付中心由 PayCenterNotify 消费支付成功、支付异常、退款、白条退回和分期等事件。具体 destination/routing key 和 payload 在 MQ 专题统一维护。
15. 核心表和读写矩阵
| 物理表 | 关键字段 | 业务作用 | 典型读写时机 |
|---|---|---|---|
t_scm_po_order | id、billNo、sid、billStatus、orderType、来源三字段 | 采购主单 | 保存/提交新增更新;回调、关闭、完成更新 |
t_scm_po_order_ext | 采购单 ID、配送/地址扩展 | 主单扩展 | 急件客户配送、特殊业务保存 |
t_scm_po_order_info | 主单 ID、商品、采购数量、价格 | 采购明细快照 | 保存草稿、提交、详情、退货关联 |
t_scm_po_order_info_qty | 采购明细 ID、数量分类 | 发货/入库/关闭等数量拆分 | 出库、入库、关闭、退货累计 |
t_scm_po_order_payinfo | 采购单、支付单、支付方式/金额 | 采购支付关联 | 发起或支付回调落关联 |
t_scm_po_out_info | 采购明细、发货数量、物流 | 供应方发货事实 | 出库/配送消息写入,入库查询 |
t_scm_po_close_info | 来源采购单/明细、关闭数量 | 未履约关闭事实 | 明细/整单关闭和补偿查询 |
t_scm_payment | 付款单号、服务站、状态、总额 | 付款主单 | 支付回调创建/更新 |
t_scm_payment_info | 付款单、采购单、账户、金额 | 付款分录 | 支付回调写明细和账户 |
t_scm_inventory | sid、单号、transType、业务日期 | 库存业务主事实 | 采购入库/退货出库写入 |
t_scm_inventory_info | 商品、仓库、货位、数量、成本 | 库存流水明细 | 每次库存变动新增 |
t_scm_inventory_real_time | sid、SKU/物料、仓库、货位、实时数量 | 当前库存结果 | 入库增加、退货出库减少 |
16. 数据关系图
erDiagram
SCM_PO_ORDER ||--|{ SCM_PO_ORDER_INFO : order_id
SCM_PO_ORDER ||--o| SCM_PO_ORDER_EXT : order_id
SCM_PO_ORDER_INFO ||--o{ SCM_PO_ORDER_INFO_QTY : order_info_id
SCM_PO_ORDER ||--o{ SCM_PO_ORDER_PAYINFO : order_id
SCM_PO_ORDER_INFO ||--o{ SCM_PO_OUT_INFO : order_info_id
SCM_PO_ORDER_INFO ||--o{ SCM_PO_CLOSE_INFO : src_order_info_id
SCM_PO_ORDER ||--o{ SCM_PAYMENT_INFO : stl_id
SCM_PAYMENT ||--|{ SCM_PAYMENT_INFO : payment_id
SCM_PO_ORDER ||--o{ SCM_INVENTORY : source_order
SCM_INVENTORY ||--|{ SCM_INVENTORY_INFO : inventory_id
旧表字段命名同时存在驼峰和下划线,ER 图使用业务语义名。写 SQL 前必须以目标环境表结构和模型字段为准。
17. 事务、锁和幂等边界
| 场景 | 当前保护 | 仍需注意 |
|---|---|---|
| 保存草稿 | 服务端校验后保存主明细 | 重复点击可能生成多个草稿,前端需使用返回 ID |
| 修改草稿 | 强制状态为草稿 | 提交与修改并发时依赖最新状态校验 |
| 待支付取消 | sid+id Redis 锁 | 锁不是账务幂等,支付回调并发仍需事务判断 |
| 明细/整单关闭 | 请求内容 MD5 Redis 锁 + 状态/数量校验 | 不同 payload 可能获得不同锁,数据库数量条件是最后防线 |
| 采购入库 | sid+md5(goodsList) Redis 锁 30 秒 | 锁过期/不同排序 payload 仍可能重复,必须核对已入库量 |
| 支付回调 | 数据库事务、支付单/采购状态判断 | 重复回调必须不重复记账和扣额度 |
| 订单中心回调 | 消费 ACK/NACK 和业务状态判断 | payload 幂等键、重试次数需在 MQ 专题确认 |
资金、库存和来源活动同时变化的操作属于高风险链路。禁止只改一个主状态来“修好页面”。
18. 按采购单号排查 SOP
18.1 主单、明细和来源
SELECT id, billNo, sid, billStatus, orderType, paymentType,
closeStatus, srcOrderType, srcOrderId, srcOrderNo,
paymentTime, createTime, modifyTime, isDelete
FROM t_scm_po_order
WHERE sid = :sid AND billNo = :po_bill_no;
SELECT *
FROM t_scm_po_order_info
WHERE sid = :sid AND orderId = :po_order_id
ORDER BY id;
SELECT *
FROM t_scm_po_order_info_qty
WHERE sid = :sid AND orderId = :po_order_id
ORDER BY orderInfoId, id;
先确认 orderType 和来源三字段,再决定查普通采购、活动、预订单还是退货链路。
18.2 支付
SELECT *
FROM t_scm_po_order_payinfo
WHERE sid = :sid AND orderId = :po_order_id
ORDER BY id;
SELECT p.id, p.billNo, p.billStatus, p.totalAmount, p.createTime,
i.stlId, i.stlNo, i.amount, i.accId, i.wayId, i.createTime AS infoCreateTime
FROM t_scm_payment p
JOIN t_scm_payment_info i ON i.billId = p.id AND i.sid = p.sid
WHERE i.sid = :sid AND (i.stlId = :po_order_id OR i.stlNo = :po_bill_no)
ORDER BY p.id, i.id;
若字段名与环境不一致,以模型和 SHOW COLUMNS 的只读结果校正。核对支付中心回调时间、采购 paymentTime、付款分录和来源单状态是否同时推进。
18.3 发货、关闭和库存
SELECT * FROM t_scm_po_out_info
WHERE sid = :sid AND srcOrderId = :po_order_id
ORDER BY id;
SELECT * FROM t_scm_po_close_info
WHERE sid = :sid AND srcOrderId = :po_order_id AND isDelete = 0
ORDER BY id;
SELECT i.id, i.billNo, i.transType, i.billDate, d.*
FROM t_scm_inventory i
JOIN t_scm_inventory_info d ON d.billId = i.id AND d.sid = i.sid
WHERE i.sid = :sid AND (i.srcOrderId = :po_order_id OR i.srcOrderNo = :po_bill_no)
ORDER BY i.id, d.id;
18.4 代码定位
rg -n "function saveInvPo|function submitInvPo|function cancelWaitPayOrder" application
rg -n "function closeInvPo|function invPoInStorage|function invPoOutStorage" application
rg -n "TYPE_ORDERCENTER_ORDER_AUDIT|paycenterPayResult" application/controllers/tasks application/KzData/Enums
rg -n "SRC_ORDER_TYPE_FLASH_ACTIVITY|ORDERTYPE_FLASH_ORDER" application
19. 常见故障决策表
| 现象 | 先查 | 再查 | 常见原因 |
|---|---|---|---|
| 草稿保存失败 | 权限、仓库、订单类型 | 商品校验和供应关系 | 移动仓、物料停用、起订量、急件地址 |
| 草稿已提交仍能编辑/编辑失败 | billStatus | 提交与页面缓存 | 状态已不是 0 或并发提交 |
| 支付成功仍待支付 | 支付回调日志 | 付款三表和采购事务 | 回调失败、来源处理抛错、重复回调冲突 |
| 审核一直不推进 | 订单中心消息 | 消费 ACK/NACK、单号映射 | 外部未回调或业务异常 NACK |
| 不能关闭 | 主状态和可关闭量 | 特殊订单类型/来源 | 已出库、赠品/秒杀限制、token 或锁冲突 |
| 入库提示超量 | 采购数量拆分 | 发货、关闭、历史入库 | 重复入库或关闭量未扣除 |
| 库存增加但采购未完成 | 库存流水 | 采购明细已入库量和事务 | 跨表更新部分失败或历史修复不完整 |
| 报表超入/数量不一致 | 业务四类数量事实 | Hologres 同步时间和口径 | 只修业务表、同步延迟、状态过滤不同 |
| 不同服务站退货行为不同 | 新采购服务开关 | RefundProvider/本地 Service | 灰度路由差异 |
20. 改动风险和回归清单
高风险公共点
InvPoService.php:关闭、入库、退货、支付方法调用面广。PoOrderSer.php:普通、活动、预订单、关闭和来源释放共用。PayCenterNotify.php:资金、采购状态、账户、来源业务同事务联动。InventorySer.php:所有库存方向共用,交易类型或正负号错误会扩散。PoOrderEnums.php:状态和订单类型被页面、任务、报表、MQ 共同使用。
最小回归集
- 普通采购保存、修改、删除、提交;移动仓被拒绝。
- 急件配送到站/客户、自提,联系人地址校验正确。
- 在线支付成功、失败、重复回调、超时取消;付款分录不重复。
- 线下挂账提交后不错误进入在线支付。
- 订单中心审核通过、驳回、取消、重复消息。
- 明细部分关闭、整单关闭、已有出库量时关闭被限制。
- 入库一次、分批入库、重复点击、超量、错误货位、发货差异。
- 不良品、滞销品、未发货、直采和首配退货的新旧服务路径。
- 秒杀/赠品/预订单来源在支付和关闭后正确回写。
- 采购主明细、数量拆分、关闭、库存流水和报表口径核对。
21. 证据索引和待确认项
已确认
- PC 请求入口:
application/controllers/scm/InvPo.php。 - 请求校验:
application/Validate/Po/InvPoValidate.php。 - 草稿保存:
PoOrderSer::checkOrderGoods、NormalOrderSaveService::savePoOrder。 - 关闭/入出库入口:
InvPoService::closeInvPo、closeInvPoOrder、invPoInStorage、invPoOutStorage。 - 支付入口和回调:
InvPo支付方法、tasks/PayCenterNotify.php。 - 订单中心事件:
tasks/OrderCenterNotify.php。 - App/WMS code:
application/config/appapis.php。 - 状态和类型:
application/KzData/Enums/PoOrderEnums.php。 - 表名:
application/config/tables.php。
待代码/环境确认
- 当前分支
InvPoService::submitInvPo的真实定义位置和提交内部调用链。 - 各环境 PC、App、新采购服务的流量切换规则和真实网关 URL。
- 支付、订单中心和采购服务的消息幂等键及生产重试策略。
- 采购入库单物理表、库存主单来源字段在各历史版本的实际字段名。
- 报表中心同步频率和采购完成状态过滤口径。
这些缺口必须在对应专项文章继续回填;在确认前不要把推测当成生产事实。
请求-日志-数据变更追踪卡
多入口请求链路
| 场景 | 调用方与入口 | 请求载荷/上下文 | Controller/Consumer | Service/Provider | 汇合点 | 最终业务事实 |
|---|---|---|---|---|---|---|
| PC 新建/提交采购 | PC 采购页 scm/InvPo/* | sid、供应商、仓库、商品数量、价格、付款方式 | controllers/scm/InvPo.php | PoOrderSer、NormalOrderSaveService、InvPoService | 采购 billNo | 采购主表/明细创建并进入合法状态 |
| App/WMS 入库 | appapis.php 采购/仓储 API code | 采购单号、SKU、仓位、实收数量 | App API 映射 Controller | InvPoService::invPoInStorage、InventorySer | 采购单号 + 入库业务单号 | 入库数量、采购状态和库存同步增加 |
| 支付中心回调 | DEST_PAYCENTER_NOTIFY | payOrderNo、来源单号、payStatus、金额 | tasks/PayCenterNotify.php | PaymentModel、PaymentInfoModel、PoOrderPayInfoModel | 采购单号/支付单号 | 支付流水落库,采购待支付状态推进 |
| 订单/配送中心通知 | ODC/配送 MQ | 来源单号、发货/关闭/签收结果 | OrderCenterNotify.php、DispatchCenterNotify.php | 采购关闭、出入库、物流 Service | 内部单号 + 外部消息业务键 | 发货、关闭、退货状态及数量更新 |
| 秒杀派生采购 | 秒杀服务站下单 | 秒杀订单、活动商品、收货信息 | FlashSaleSer::submitOrder | createPoOrdersForFlashOrder、采购 Service | FLASH_ORDER 与采购单关系 | 每个供给关系生成采购单并等待支付/履约 |
日志证据矩阵
| 链路段 | 日志来源 | 可检索锚点 | 成功信号 | 失败信号 | 与下一段关联方式 |
|---|---|---|---|---|---|
| PC 请求 | Web/CI 日志、InvPo 方法 | URI、sid、采购 billNo、供应商 | 返回采购主键/单号 | 校验失败、状态冲突、价格或库存异常 | 单号查 SCM_PO_ORDER 与明细 |
| 保存/提交 | PoOrderSer、NormalOrderSaveService、InvPoService 异常 | 类方法、billNo、订单类型 | 主明细提交且事务 commit | rollback、重复单、非法状态 | 主表状态 + 明细数量/金额 |
| 支付消费 | PayCenterNotify::paycenterPayResult | payOrderNo、来源采购单号、payStatus=02 | 三类支付记录生成/更新 | 非 02、金额/订单不匹配、消费异常 | 支付单号关联 SCM_PO_ORDER_PAYINFO |
| 入库与库存 | invPoInStorage、InventorySer | 采购单号、SKU、transType=150501 | 实收量和实时库存同时增加 | 超收、重复入库、仓位无效、回滚 | 单号查采购明细、库存流水、实时表 |
| 异步关闭/发货 | Order/Dispatch Consumer | 消息类型、来源单号、消息 ID | ACK 且订单状态/数量符合消息 | NACK、重复、顺序倒置 | 消息键 + 单号回查状态历史/业务表 |
环节数据变更台账
| 步骤 | 代码位置 | 事务 | 读取事实 | 写入表/缓存/MQ | 字段或数量变化 | 回查证据 |
|---|---|---|---|---|---|---|
| 保存草稿 | NormalOrderSaveService::savePoOrder | 采购事务 | 供应商、仓库、商品、价格 | SCM_PO_ORDER、SCM_PO_ORDER_EXT、SCM_PO_ORDER_INFO | 新增主明细;status=0;数量/金额写入 | sid + billNo + 明细 SKU |
| 提交审核/履约 | InvPoService、PoOrderSer | 采购事务 | 当前 status、支付方式、订单类型 | 采购主表、地址/扩展、可能的 MQ | status: 0 -> 1/2/10/13,具体按类型和支付路径 | 主表状态、提交时间、MQ 业务键 |
| 支付成功 | PayCenterNotify | 回调本地事务 | 回调状态、采购状态、既有支付记录 | SCM_PAYMENT、SCM_PAYMENT_INFO、SCM_PO_ORDER_PAYINFO、采购主表 | 支付金额累计;待支付 10 -> 后续履约态;重复回调不应重复累计 | payOrderNo、采购单号、三表金额 |
| 发货/出库 | InvPoService::invPoOutStorage 等 | 履约事务 | 可发数量、当前状态 | SCM_PO_OUT_INFO、采购明细/主表 | outQty: old -> old+n;状态进入待入库/配送 | 单号、SKU 发货累计与消息回执 |
| 采购入库 | InvPoService::invPoInStorage -> InventorySer | 入库与库存事务边界需环境确认 | 采购/发货可收量、仓位、实时库存 | 采购入库事实、SCM_INVENTORY* | inQty: old -> old+n;库存 qty: old -> old+n;完成后状态 -> 5 | 采购数量等式 + transType=150501 流水 |
| 关闭/退货 | closeInvPo*、退货 Service | 关闭/退货事务 | 未履约量、已支付/已入库量 | SCM_PO_CLOSE_INFO、退货/物流表、库存/退款消息 | 未履约量关闭;退货库存按 170402/对应入出库类型反向变化 | 关闭明细、退货单、退款单、库存流水 |
子模块级独立追踪
以下每张卡只描述一个采购子模块。排查时先用稳定业务键选中卡片,再沿同一行从请求、代码、数据库走到日志和补偿,不把相邻采购动作混为一次事务。
子模块追踪:po-draft 采购草稿保存与编辑
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 保存 | PC scm/InvPo/saveInvPo,postData JSON | request ID、sid、供应商、仓库、SKU;返回采购 id/billNo | application/controllers/scm/InvPo.php -> PoOrderSer::checkOrderGoods -> NormalOrderSaveService::savePoOrder | 商品状态、供应关系、采购价、仓库和移动仓规则 | 采购事务内 insert SCM_PO_ORDER/EXT/INFO;status=0,订购量和金额由无记录 -> 草稿快照 | URI + request ID 找 Controller;再用 sid+billNo 查主明细创建时间 | 校验失败零写入;事务异常 rollback;未知结果先按 sid+billNo/SKU 查重,禁止盲目重提 |
| 编辑 | PC updateInvPo,载荷含草稿 id | request ID、sid、采购 id/billNo、新商品集合 | application/controllers/scm/InvPo.php -> 草稿查询 -> 商品重校验 -> 保存 Service | 主单必须仍为 status=0;读取旧明细、价格和数量 | 同一采购事务 update 主表;明细按代码增删改,数量/金额 old -> new;非草稿不写 | request ID + billNo 对照更新前后 updated_at、明细主键和合计 | 并发提交导致状态已变时拒绝编辑;不手工覆盖状态,按最新主单重新加载页面 |
子模块追踪:po-submit-audit 采购提交与审核
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 提交 | PC submitInvPo 或对应 App API code | request ID、sid、orderId/billNo、订单类型、支付方式 | application/controllers/scm/InvPo.php -> InvPoService::submitInvPo;具体实现位置仍按当前分支确认 | 草稿状态、明细有效性、支付/审核路径、来源类型 | 采购事务内 status: 0 -> 1/2/10/13 中符合分支的一种;写提交时间,commit 后可能发订单中心消息 | request ID + billNo + Controller/Service 方法;MQ 用来源单号/消息 ID 续查 | 状态条件更新 0 行视为并发冲突;本地 commit 后 MQ 失败只补消息,不重复提交采购 |
| 审核回调 | 订单中心 DEST_ODC_NOTIFY 审核事件 | message ID、来源 billNo、外部订单号、审核结果 | application/controllers/tasks/OrderCenterNotify.php -> 采购审核处理 Service | 当前 status、订单中心映射、消息是否已处理 | 单消息事务内 status: 1/2 -> 3/8 等合法目标;拒绝写原因;重复结果应为 new -> new 零副作用 | destination/event + message ID + 来源单号;回查主表状态、审核时间和外部关系 | 未知事件/找不到关系应 NACK 或告警;旧回调不得把终态回退,人工补偿前以订单中心最终态仲裁 |
子模块追踪:po-payment 采购支付与支付回调
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 发起支付 | PC noPagePay/unionPayCode/exclusivePay | request ID、sid、采购 billNo、金额、支付方式;支付中心返回 payOrderNo | application/controllers/scm/InvPo.php -> application/service/scm/InvPoService.php -> 支付 Provider | 采购应付、已付、待付状态和账户/授信条件 | 本地与支付中心不是同一事务;保存或返回支付关系,采购通常保持 status=10 等待最终回调 | request ID + billNo + payOrderNo 串联 Provider 请求和支付中心查询 | HTTP 超时不能直接判失败;先用 payOrderNo/billNo 查询支付中心和本地关系,再决定重试 |
| 成功回调 | DEST_PAYCENTER_NOTIFY,payStatus=02 | message ID、payOrderNo、来源采购单号、金额 | application/controllers/tasks/PayCenterNotify.php::paycenterPayResult -> Payment/PoOrderPayInfo Model -> 来源 Service | 当前采购状态 10/12、既有支付主明细和支付关系 | 回调事务 insert/update SCM_PAYMENT/INFO、SCM_PO_ORDER_PAYINFO;已付金额 old -> old+n,采购进入后续履约态;同支付单重复为 0 增量 | message ID + payOrderNo + billNo;三表金额、采购状态和 ACK 同时取证 | 迟到支付先核对此前关闭/库存释放;本地失败按原 payOrderNo 幂等补偿,禁止新建随机支付单重复入账 |
子模块追踪:po-delivery 采购发货与配送
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 供应方发货 | 订单/采购/配送中心发货事件 | message ID、采购 billNo、SKU、发货量、物流单 | application/controllers/tasks/OrderCenterNotify.php 或 DispatchCenterNotify.php -> 采购发货 Service | 订购量、已发量、关闭量、当前 3/4/13 状态 | 消费事务 insert/update SCM_PO_OUT_INFO;outQty: old -> old+n,部分/全部发货推进主状态 | event + message ID + billNo+SKU;对照发货明细、主状态和 ACK | 超发或旧状态拒绝;重复消息按来源发货业务键幂等,不能只凭随机 message ID 去重 |
| 配送回写 | 配送中心揽收、运输、到达/签收通知 | message ID、采购单、配送/运单号、外部状态 | application/controllers/tasks/DispatchCenterNotify.php -> 物流/采购 Service | 本地发货关系和当前物流状态 | 本地事务 update 物流/关系状态 old -> next;不直接增加库存,库存要等实际入库 | 外部配送号 + 采购单 + message ID;两端按绝对时间对齐 | 回调乱序不得状态回退;外部已成功本地失败只补关系/状态,不重新创建配送单 |
子模块追踪:po-inbound 采购入库与库存增加
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 入库确认 | PC invPoInStroage、App/WMS 采购入库 API | request ID、sid、采购 billNo/orderId、SKU、仓库、货位、实收量 | application/controllers/scm/InvPo.php -> InvPoService::invPoInStorage -> InventorySer::save/changeInventoryQty | 可收=订购-已收-关闭 或按发货口径;仓位和实时库存四维行 | 入库与库存事务边界需按调用点确认;采购 inQty: old -> old+n,写 SCM_INVENTORY/INFO 的 transType=150501,实时 qty: old -> old+n | request ID + billNo+SKU+transType;同时查采购明细、库存主分片和实时四维键 | 超收、仓位无效或 Redis 锁冲突时不应部分写;若采购量与库存部分成功,按原入库业务键补断点,禁止再做整单入库 |
| 完成判定 | 每次入库事务尾部 | billNo、每 SKU 订购/发货/已收/关闭量 | application/service/scm/InvPoService.php 的入库完成判断 | 全部明细的数量等式和当前主状态 | 满足全部履约时主状态 old -> 5;未全部满足保持待入库/配送态;无额外库存变化 | billNo 查所有明细数量合计和主表更新时间 | 主状态与数量不一致时以过程事实为准;先修缺失入库/关闭事实,再由领域逻辑重算状态 |
子模块追踪:po-close 采购关闭与取消
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 明细/整单关闭 | PC closeInvPo/closeInvPoOrder/cancelWaitPayOrder | request ID、sid、采购 billNo/id、关闭 SKU/数量、token | application/controllers/scm/InvPo.php -> Redis 锁 -> InvPoService::closeInvPo* 或 PoOrderSer::cancelWaitPayOrder | 当前状态、未发未收量、已支付和特殊来源限制 | 关闭事务 insert SCM_PO_CLOSE_INFO;closeQty: old -> old+n;全部关闭时 status -> 9/12 等代码目标 | request ID + token/锁 key + billNo+SKU;回查关闭明细、主状态和影响行数 | 锁过期不等于可重做;先查关闭明细。支付/来源释放在事务外失败时仅补对应后置副作用 |
| 外部关闭回调 | 订单中心采购关闭/取消事件 | message ID、来源采购单、关闭原因/明细 | application/controllers/tasks/OrderCenterNotify.php -> 采购关闭 Service -> MqSer 来源通知 | 本地已履约/已关闭量和外部最终态 | 单消息事务补关闭量和主状态;commit 后通知秒杀/预订单等来源释放,DB 状态 old -> closed | event + message ID + billNo;来源订单 ID 用于下一段日志 | 外部已关本地失败按来源单号重放;来源释放失败不回滚已提交采购关闭,交由来源补偿任务处理 |
子模块追踪:po-return 采购退货与退货出库
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 创建提交退货 | PC saveOrderBack*、submitOrderBack 或新售后入口 | request ID、sid、原采购单、退货单、SKU、申请量、原因 | application/controllers/scm/InvPo.php / application/controllers/po/AfterSale.php -> RefundProvider 或老退货 Service | 原采购已入库、已申请/已退数量、站点新旧服务开关 | 退货事务 insert/update 退货主明细和原单关系;申请量由 0 -> n,状态草稿 -> 待审核 | request ID + 原采购/退货单号 + SKU;新服务再关联外部售后单 | 超可退量、秒杀限制或来源不符时零写入;新服务超时先查外部售后单,避免重复申请 |
| 退货出库 | PC invPoOutStorage/PDA 仓内动作 | request ID、退货 billNo/orderId、SKU、仓库货位、实出量 | application/controllers/scm/InvPo.php -> InvPoService::invPoOutStorage -> InventorySer | 审核通过状态、可出退货量、实时库存 | 退货出库事务 returnOutQty: old -> old+n;写采购退货库存流水,实时库存 qty: old -> old-n;物流状态初始化 | 退货单 + SKU + transType + 库存四维键;对照物流关系 | 重复扫描/消息必须 0 增量;库存已扣但退货量未写时按原退货出库键补业务明细,不再次扣库 |
子模块追踪:po-source 秒杀、赠品与预订单来源联动
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 来源建采购 | 秒杀、赠品、预订单 Service 创建采购 | 来源 ID/单号、srcOrderType、orderType、sid、SKU、数量 | application/Services/Activity/FlashSaleSer.php、预订单/赠品 Service -> 采购保存 Service | 来源订单状态、锁定/可提额度、商品供给与地址 | 跨域事务边界需分别核对;insert SCM_PO_ORDER*,写 srcOrderId/srcOrderNo/srcOrderType;来源关系由无 -> 有 | 来源单号 + 采购 billNo + source type;两边关系和数量同时回查 | 采购创建失败必须回滚/释放来源锁量;不允许只补一张无来源关系的采购单 |
| 支付关闭回传 | 采购支付成功、关闭或退款事件 | payOrderNo/message ID、采购单、来源 ID、SKU 数量 | application/controllers/tasks/PayCenterNotify.php / OrderCenterNotify.php -> PoOrderSer -> 来源领域 release/mark Service | 采购最终态、来源已用/锁定/可提数量和幂等记录 | 采购事务 commit 后异步更新来源:锁定 old-n、已用 old+n,或关闭时可用量 old+n;重复事件 0 变化 | 采购单 + 来源单 + 支付/消息 ID 串联两域日志 | 采购已成功而来源未更新时只重放来源动作;先证明支付/关闭最终态,禁止重做采购主流程 |