1. 文档目标

本文把 DGJ2 中容易混在一起的两组能力拆开说明,再解释它们如何连接采购、销售、库存和外部系统:

  1. 渠道大客户订单:懂车帝、孚创、车之谷等渠道通过 DGJ 接口查询商品、下单、审核、签收和申请售后;DGJ/OPS 负责人工开单、发货、退货入库及结果通知。
  2. 全车件:DGJ 提供登录态、功能开通校验和统一路由,把采购、销售、库存、资金等请求转发到全车件微服务;部分导入、导出、打印在 DGJ 本地完成。

本文适用于:

  • 接手渠道订单、渠道退货或全车件需求;
  • 排查外部订单已创建但 DGJ 不显示;
  • 排查渠道订单不能审核、不能发货或未自动发货;
  • 排查退货数量、原出库单拆分或退货入库异常;
  • 排查渠道方、OPS 没有收到状态通知;
  • 排查全车件接口 404、未开通、转发失败、导入或打印失败;
  • 修改公共大客户出库逻辑前确认影响范围。
本文结论来自当前代码静态核对。网关域名、鉴权签名和 Apollo 实际配置值属于环境事实,必须在对应环境再次确认。

2. 先看结论

2.1 一张图理解模块边界

flowchart LR
    EXT["外部渠道\nDCD / FC / CZG"]
    INNER["inner/channel\n商品、订单、售后 API"]
    CHANNEL["Channel Service\n场景工厂 + 公共流程"]
    CT["渠道业务表\nt_channel_order / aftersale"]
    OPS["DGJ / OPS\nscm/InvCu"]
    BIG["大客户单据\n180601 出库 / 180602 退货"]
    INV["库存、货位、成本价"]
    MQ["dgj_channel MQ"]

    ACPUI["DGJ 全车件页面"]
    ACP["allcarpart/* 门面"]
    MICRO["全车件微服务\nMICRO_URL"]

    EXT --> INNER --> CHANNEL --> CT
    OPS --> CHANNEL
    CHANNEL --> BIG --> INV
    CHANNEL --> MQ --> OPS
    EXT <-->|"创建结果 / 签收 / 售后"| INNER

    ACPUI --> ACP --> MICRO
    ACP -. "导入、打印、导出\n存在本地补充处理" .-> INV

2.2 五个不能混淆的编号

名称示例前缀/来源保存位置作用
渠道来源订单号外部渠道生成t_channel_order.source_order_no外部系统识别原采购订单,也是创建幂等依据
DGJ 渠道订单号CHSt_channel_order.order_noDGJ 内部平台订单主标识,发货和售后关联使用
渠道来源明细行号外部渠道生成t_channel_order_info.source_row_no对齐外部订单明细
大客户出库单号DGJ 大客户单据生成t_scm_big_order.billNo实际库存出库事实,交易类型为 180601
DGJ 渠道退单号CHRt_channel_aftersale.refund_noDGJ 内部售后标识,退货入库和 MQ 通知使用

排查时至少同时收集:sceneCode、shopId、外部订单号、CHS、CHR、大客户出库单号、sid。

2.3 三个渠道的关键差异

能力懂车帝 dcd孚创 fc车之谷 czg
来源类型567
外部渠道直接创建支持支持支持
创建后初始状态wait_shipcreatedcreated
渠道审核接口不支持支持支持
DGJ 人工创建不支持支持支持
OPS 详情转换不支持支持支持
审核后自动发货无此分支受 fc_auto_send 控制受 czg_auto_send 控制
售后公共流程支持支持支持

初始状态差异来自具体实现:

  • DcdOrderService::create() 使用 BaseOrder::formatRequestToOrderEntry() 的默认 wait_ship。
  • FC/CZG 复用 ChannelOrderTrait::create(),创建后主动覆盖为 created,必须再走渠道审核。

3. 代码入口地图

3.1 外部渠道入口

业务控制器方法下游 Service
商品列表application/controllers/inner/channel/Goods.phplists()场景对应 *GoodsService::lists()
商品详情同上detail()场景对应 *GoodsService::detail()
渠道创建订单application/controllers/inner/channel/Order.phpcreate()场景对应 *OrderService::create()
渠道审核订单同上order_approve()FC/CZG ChannelOrderTrait::order_approve()
渠道确认签收同上confirm_receipt()BaseOrder::confirm_receipt()
渠道申请售后application/controllers/inner/channel/Aftersale.phpapply()BaseAftersale::apply()
查询退货入库同上get_return_stock_in()当前仅返回空成功结果

控制器的相对路径可推导为:

  • inner/channel/goods/lists
  • inner/channel/goods/detail
  • inner/channel/order/create
  • inner/channel/order/order_approve
  • inner/channel/order/confirm_receipt
  • inner/channel/aftersale/apply
  • inner/channel/aftersale/get_return_stock_in

生产实际 URL、HTTP 方法、签名和网关前缀需要以网关配置或联调文档为准;本文只确认应用内控制器路径和请求字段。

3.2 DGJ/OPS 操作入口

业务控制器方法Service 方法
人工创建 FC/CZG 渠道订单scm/InvCu::channelOrderManualCreate()场景 Service manual_create()
渠道订单列表scm/InvCu::channelOrder()BaseOrder::lists()
渠道订单详情scm/InvCu::channelOrderDetail()BaseOrder::detail()
渠道订单发货scm/InvCu::channelOrderDelivery()BaseOrder::delivery()
渠道售后列表scm/InvCu::channelAftersale()BaseAftersale::lists()
渠道售后详情scm/InvCu::channelAftersaleDetail()BaseAftersale::detail()
退货通过并入库scm/InvCu::channelAftersalePass()BaseAftersale::refund_pass()
拒绝退货scm/InvCu::channelAftersaleReject()BaseAftersale::refund_reject()

3.3 核心 Service

文件职责
application/Services/Channel/SceneConst.php场景字典、Service 工厂映射、Apollo 配置读取
application/Services/Channel/BaseService.php渠道公共依赖和基础辅助能力
application/Services/Channel/BaseGoods.php商品搜索、详情、库存和属性组装
application/Services/Channel/BaseOrder.php列表、详情、发货、签收、渠道单到大客户出库转换
application/Services/Channel/BaseAftersale.php售后申请、原出库拆分、退货入库、拒绝
application/Services/Channel/Dcd/DcdOrderService.php懂车帝直接创建逻辑
application/Services/Channel/Traits/ChannelOrderTrait.phpFC/CZG 人工开单、创建、审核、复制、自动发货
application/Services/Mq/MqSer.php渠道 MQ 生产者
application/controllers/tasks/ChannelOrderNotify.phpdgj_channel 消费者及 OPS/自动发货处理
application/service/scm/InvCuService.php大客户出库/退货、OPS 推送和大客户类型

3.4 场景服务选择

sequenceDiagram
    participant C as 渠道请求
    participant B as BaseChannel
    participant R as ContactSer
    participant S as SceneConst
    participant I as 具体 Service

    C->>B: sceneCode + shopId + 业务参数
    B->>B: 校验 sceneCode 为 dcd/fc/czg
    B->>R: getStationAndContactBySourceId(shopId, sceneCode)
    R-->>B: stationInfo + contactInfo
    alt 未建立绑定
        B-->>C: 服务站和修理厂绑定信息异常
    else 绑定存在
        B->>S: 根据 sceneCode 读取 Goods/Order/Aftersale 类
        S-->>B: 具体 Service 类名
        B->>I: 执行场景实现
        I-->>C: 统一响应
    end

BaseChannel 构造时就要求 sceneCode 和 shopId,并解析:

  • 渠道映射的服务站 sid;
  • 渠道客户对应的 DGJ contactId;
  • 服务站和客户基础信息。

因此,业务方法还没执行就报“绑定信息异常”时,应先查渠道客户关系,不要先查订单表。

4. 渠道商品接口

4.1 商品列表请求

应用内入口:inner/channel/goods/lists。

示意请求:

{
  "sceneCode": "dcd",
  "merchantCode": "渠道商家编码",
  "shopId": "渠道客户标识",
  "keywords": "刹车片",
  "skuCategoryCodes": ["分类编码"],
  "carCode": "车型层级编码",
  "page": 1
}

处理过程:

  1. BaseChannel 根据 shopId + sceneCode 获取 DGJ 登录上下文。
  2. BaseGoods::lists() 组装数据平台检索参数。
  3. OfferProvider::searchList() 查询候选商品和库存。
  4. 使用快准侧 Mongo 物料补商品名称、分类等基础信息。
  5. 返回 skuCode、skuName、分类、图片和 inventoryQty。

搜索最大条数默认 100,可由 Redis 键 S_CHANNEL_MAX_SEARCH 覆盖。这里返回的是渠道可见商品结果,不等于最终下单一定成功;下单还会重新校验服务站物料和成本价。

4.2 商品详情请求

应用内入口:inner/channel/goods/detail。

{
  "sceneCode": "fc",
  "merchantCode": "渠道商家编码",
  "shopId": "渠道客户标识",
  "skuCodes": ["SKU001", "SKU002"]
}

详情链路:

flowchart LR
    API["goods/detail"] --> BIND["解析 sid/contactId"]
    BIND --> MONGO["服务站 Mongo 物料\n名称、fineQty"]
    BIND --> VIN["VinProvider\n属性、图片"]
    MONGO --> MERGE["按 skuCode 合并"]
    VIN --> MERGE
    MERGE --> OUT["skuList\n库存、图片、attributes"]

详情最多截取 maxLimit 个 SKU。物料在服务站 Mongo 中不存在时,该 SKU 会被跳过。

5. 渠道创建订单接口

5.1 请求字段

应用内入口:inner/channel/order/create。

{
  "sceneCode": "fc",
  "merchantCode": "渠道商家编码",
  "merchantName": "渠道商家名称",
  "shopId": "渠道客户标识",
  "orderNo": "外部渠道订单号",
  "submitTime": "2026-07-15 10:00:00",
  "totalAmount": "200.00",
  "remark": "渠道备注",
  "details": [
    {
      "orderRowNo": "外部明细行号",
      "skuCode": "SKU001",
      "qty": 2,
      "price": "100.00",
      "amount": "200.00",
      "remark": "明细备注"
    }
  ]
}

关键字段规则来自 OrderCreateValidate:

字段规则业务含义
sceneCodedcd/fc/czg选择具体渠道实现
shopId必填解析 DGJ 服务站和客户绑定
orderNo必填,最长 50外部来源订单号,也是创建幂等依据
submitTime合法日期外部渠道提交时间
totalAmount必填外部声明总金额;代码会按明细重新计算实际总额
details非空数组至少包含行号、SKU、数量、单价、小计
price不小于 0.01订单销售单价
amount必填必须等于 price * qty,保留两位比较

5.2 创建主流程

sequenceDiagram
    participant EXT as 外部渠道
    participant CTRL as Order::create
    participant BIND as BaseChannel/ContactSer
    participant SER as DCD 或 FC/CZG Service
    participant MONGO as 服务站物料 Mongo
    participant COST as CuOrderMaterielSer
    participant DB as 渠道订单表

    EXT->>CTRL: 创建请求
    CTRL->>BIND: sceneCode + shopId
    BIND-->>CTRL: sid + contactId
    CTRL->>CTRL: 请求哈希 Redis 短锁 + 参数校验
    CTRL->>SER: create(request + stationInfo + contactInfo)
    SER->>DB: 按 source_order_no 查重
    alt 已存在
        DB-->>SER: 返回现有 CHS,不重复写表
    else 首次创建
        SER->>MONGO: 按 skuCode 查询服务站物料
        SER->>COST: 按 invId 查询服务站成本价
        SER->>SER: 校验数量、价格、小计并计算成本/价差
        SER->>DB: 事务写主表和明细表
    end
    SER-->>CTRL: CHS 订单号
    CTRL->>CTRL: createCarType(merchantCode)
    CTRL-->>EXT: saleOrderNo + sourceOrderNo + 站点和明细

5.3 数据计算

每个明细重新计算:

sale_amount   = qty * price
cost_amount   = qty * purPrice
profit        = price - purPrice
profit_amount = qty * profit

订单汇总:

total_amount  = 所有明细 sale_amount 之和
cost_amount   = 所有明细 cost_amount 之和
profit_amount = total_amount - cost_amount

业务界面把这部分价差称为“配送费”,它不等于财务核算意义上的最终净利润。

5.4 创建落表

业务数据主表字段明细字段
场景scene_codescene_code
服务站/客户sid, contact_idsid, contact_id
外部客户source_shop_idsource_shop_id
外部订单source_order_nosource_order_no, source_row_no
DGJ 渠道订单order_noorder_no, chan_order_id
商品汇总 goods_numsku_code, sku_name, inv_id, qty
金额total_amount, cost_amount, profit_amount售价、成本、金额和价差字段
进度status, shiped_numshiped_num, refund_num

5.5 创建幂等的边界

当前存在两层保护:

  1. 控制器按整个请求 JSON 的 MD5 加 1 秒 Redis 锁,拦截极短时间的完全相同请求。
  2. Service 按 source_order_no = request.orderNo 查询已存在订单,存在时直接返回原 CHS。

需要注意:代码查重没有附加 scene_code 或 shopId。如果不同渠道可能生成相同来源订单号,必须确认数据库约束和上游编号规则,否则存在跨渠道误判为同一订单的风险。

6. 人工创建与渠道审核

6.1 DGJ 人工创建 FC/CZG 订单

入口:scm/InvCu::channelOrderManualCreate()。

支持 fc 和 czg,不支持 dcd。处理过程:

  1. 校验渠道、客户、总金额和明细。
  2. 限制同一单据不能出现重复 SKU。
  3. 通过 dkh.max_submit_row 限制每单最大商品行数。
  4. 向 OPS 查询大客户结算价。
  5. 根据 dkh.max_price_rate 计算允许的最高销售价。
  6. 校验服务站物料存在且库存大于零。
  7. 系统生成外部风格的 orderNo 和每个 orderRowNo。
  8. 复用 FC/CZG create() 写渠道订单,状态为 created。
  9. 发送 channel_order_manual_create MQ,消费者再推送 OPS。
flowchart TD
    OPSUI["DGJ 人工开单"] --> VALID["校验 FC/CZG、行数、重复 SKU"]
    VALID --> PRICE["OPS 查询结算价"]
    PRICE --> LIMIT["校验最高价 dkh.max_price_rate"]
    LIMIT --> STOCK["校验服务站物料和库存"]
    STOCK --> GEN["生成渠道订单号和行号"]
    GEN --> CREATE["复用 ChannelOrderTrait::create"]
    CREATE --> CREATED["状态 created"]
    CREATED --> MQ["channel_order_manual_create"]
    MQ --> PUSH["ChannelOrderNotify 推送 OPS"]

MQ 推送失败在人工开单方法中只记录日志,不回滚已经创建的渠道订单。因此出现“订单已存在但 OPS 没收到”时,应补偿消息或重新推送,不要再次人工创建同一业务单。

6.2 FC/CZG 渠道审核接口

应用内入口:inner/channel/order/order_approve。

{
  "sceneCode": "fc",
  "merchantCode": "渠道商家编码",
  "merchantName": "渠道商家名称",
  "shopId": "渠道客户标识",
  "saleOrderNo": "CHS...",
  "sourceOrderNo": "渠道最终订单号",
  "auditStatus": 1,
  "submitTime": "2026-07-15 10:30:00",
  "cancelReason": "拒绝时填写"
}

状态变化:

stateDiagram-v2
    [*] --> created: FC/CZG 创建
    created --> wait_ship: auditStatus = 1
    created --> cancel: auditStatus = 0
    wait_ship --> part_ship: 部分发货
    wait_ship --> finish: 一次发完
    part_ship --> part_ship: 再次部分发货
    part_ship --> finish: 累计全部发完

    [*] --> wait_ship: DCD 创建

审核约束:

  • 只有当前状态为 created 的订单可审核。
  • 只允许 fc/czg。
  • 审核通过前检查 sourceOrderNo + sceneCode 是否已存在。
  • 通过后状态为 wait_ship,拒绝后为 cancel。
  • 写入审核人、审核备注和最终来源订单号。
  • 发送 channel_order_approve MQ。

6.3 审核后自动发货

ChannelOrderNotify::channelOrderApprove() 消费审核消息后:

  1. 从消息读取 scene_code;老消息没有时按订单查询,仍没有则回退 FC。
  2. 调用具体场景 Service 的 auto_delivery()。
  3. 仅 FC/CZG 且状态为 wait_ship 才可继续。
  4. 分别检查服务站配置 fc_auto_send 或 czg_auto_send。
  5. 获取服务站默认管理账号和默认仓库。
  6. 每个商品选择第一个有足够库存的仓位结果。
  7. 组装普通 delivery() 请求,以“系统发货”执行完整出库流程。

自动发货异常在消费者中记录后返回 ACK,不会依赖 MQ 自动重试。因此配置未开、默认账号异常、默认仓库缺失、库存不足都需要业务侧重新处理或人工发货。

7. 渠道发货与库存出库

7.1 发货请求

DGJ/OPS 入口:scm/InvCu::channelOrderDelivery()。

{
  "orderNo": "CHS...",
  "delieverId": 1001,
  "delieverName": "操作人",
  "remark": "渠道订单发货",
  "details": [
    {
      "rowNo": 12345,
      "outQty": 2,
      "locationId": 10,
      "locationName": "主仓",
      "locationAreaId": 100,
      "locationAreaName": "A区",
      "remark": ""
    }
  ]
}

rowNo 是 t_channel_order_info.id,不是外部渠道明细行号。

7.2 可发校验

BaseOrder::checkCanDelivery() 依次检查:

  • 主单存在且状态为 wait_ship 或 part_ship;
  • 主单累计已发数量小于总数量;
  • 请求明细属于当前 CHS;
  • 每个明细尚未发完;
  • shiped_num + outQty <= qty;
  • 若场景在 dkh.once_delivery_merchant 中,每行和整单必须一次发完。

代码对 outQty 使用 abs(),因此业务上仍应只传正数,并由请求校验和测试限制异常符号输入。

7.3 发货数据流

sequenceDiagram
    participant OPS as DGJ/OPS
    participant BO as BaseOrder
    participant CU as InvCuService
    participant BIG as t_scm_big_order
    participant INV as 库存/货位
    participant CH as 渠道订单表
    participant MQ as dgj_channel

    OPS->>BO: orderNo + 发货明细 + 仓位
    BO->>BO: 状态、剩余数量、一次发完校验
    BO->>BO: buildBigOrderDeliverData()
    BO->>CU: submitOrderV2(postData)
    CU->>BIG: 创建 180601 大客户出库及明细
    CU->>INV: 执行出库和库存变化
    CU-->>BO: billNo
    BO->>CH: 明细 shiped_num += outQty
    BO->>CH: 主单 shiped_num += 发货总数
    BO->>CH: part_ship 或 finish
    BO->>MQ: channel_order_delivery(orderNo=billNo)
    MQ-->>OPS: 消费后推送出库信息

7.4 渠道单如何转换为大客户出库

buildBigOrderDeliverData() 生成旧大客户提交结构,关键字段如下:

大客户字段值来源说明
transType固定 180601大客户销售出库
contactId/contactName渠道主单DGJ 客户
sourceOrderCHS业务来源订单
srcOrderNo请求 orderNo同一 CHS
srcChannelOrder请求 orderNo标记渠道来源
srcOrderId渠道主单 ID写入大客户明细
srcOrderEntryId渠道明细 ID后续售后反查原出库的关键
sourceInfoRowNo外部渠道行号对齐渠道明细
location*发货请求实际扣减仓库和货位
buId/buType服务站成本价记录采购主体和类型

submitOrderV2() 未返回 data.billNo 时,渠道发货事务回滚并返回系统服务异常。只有拿到单号后才提交事务并发 MQ。

7.5 状态和数量更新

明细已发数量 = 原 shiped_num + 本次 outQty
主单已发数量 = 原 shiped_num + 本次所有 outQty 之和

主单已发数量 < goods_num  -> part_ship
主单已发数量 >= goods_num -> finish,并写 finish_time

finish 表示 DGJ 已全部发货,不等同于渠道已经回传签收。

8. 渠道签收

8.1 签收请求

应用内入口:inner/channel/order/confirm_receipt。

{
  "sceneCode": "dcd",
  "merchantCode": "渠道商家编码",
  "shopId": "渠道客户标识",
  "signingPerson": "签收人",
  "signingTime": "2026-07-15 15:00:00",
  "signingNo": "渠道签收批次号",
  "totalQty": 2,
  "details": [
    {
      "orderNo": "大客户出库单号",
      "orderRowNo": 98765,
      "skuCode": "SKU001",
      "qty": 2
    }
  ]
}

这里的 orderRowNo 是 t_scm_big_order_info.id,不是渠道明细 ID。

8.2 校验和幂等

签收前会校验:

  • 每个大客户出库明细存在;
  • 大客户明细 skuId 与请求 skuCode 一致;
  • 出库数量绝对值与签收数量一致;
  • 所有明细数量之和等于 totalQty。

唯一键计算:

unique_id = md5(signingNo + "." + shipOrderNo + "." + shipOrderRowNo)

最后调用 batchInsertOrIgnore() 写 t_channel_order_sign_detail。相同签收批次、出库单和出库明细重复回调时不会重复插入。

9. 渠道售后与退货入库

9.1 售后申请请求

应用内入口:inner/channel/aftersale/apply。

{
  "sceneCode": "dcd",
  "merchantCode": "渠道商家编码",
  "shopId": "渠道客户标识",
  "orderNo": "CHS...",
  "sourceRefundNo": "外部售后申请号",
  "submitTime": "2026-07-15 16:00:00",
  "totalAmount": "100.00",
  "remark": "退货原因",
  "details": [
    {
      "orderRowNo": 12345,
      "sourceOrderRowNo": "外部订单行号",
      "skuCode": "SKU001",
      "qty": 1,
      "price": "100.00",
      "amount": "100.00",
      "remark": "明细原因"
    }
  ]
}

RefundApplyValidate 的场景字段没有显式列出 details,但 BaseAftersale::apply() 会直接读取并深度校验明细;调用方必须传非空明细。

9.2 申请校验

每个售后明细必须满足:

  • orderRowNo 对应 t_channel_order_info.id;
  • 明细属于请求中的 CHS;
  • SKU 与原渠道订单一致;
  • 申请数量不超过原订单数量;
  • 申请价格不超过原销售价;
  • price * qty = amount;
  • 累计 refund_num + qty <= 原 qty;
  • 申请数量不超过已发数量 shiped_num。

创建成功后:

  • 生成 CHR 退单号;
  • 事务写售后主表和明细表;
  • 渠道订单明细 refund_num += 申请数量;
  • 返回 aftersaleNo=CHR 和来源售后号。

9.3 售后状态机

stateDiagram-v2
    [*] --> pass: 当前 apply() 直接创建为 pass
    pass --> finish: DGJ 审核通过并完成退货入库
    pass --> reject: DGJ 拒绝并归还 refund_num

    created --> pass: 枚举保留,但当前 apply() 未经过此状态
状态显示名称当前代码中的业务事实
created退单创建枚举存在,当前 apply() 没有落此状态
pass待收货apply() 创建后的实际初始状态,可继续通过或拒绝
reject已拒绝回退原渠道明细占用的 refund_num
finish已完成已按原出库拆分并创建 180602 退货单

9.4 为什么退货要按原出库拆分

一个渠道订单可以多次部分发货,因此一个渠道明细可能分散到多个 180601 出库明细。旧大客户退货必须关联具体原出库单和明细,所以不能直接按 CHR 创建一张总退货单。

flowchart TD
    APPLY["CHR 售后明细\n申请退 5 件"] --> FIND["按 channel_order_info.id\n查询所有原 180601 出库明细"]
    FIND --> USED["扣除每个原出库明细\n已经退回的数量"]
    USED --> A["原出库 A 可退 2"]
    USED --> B["原出库 B 可退 3"]
    A --> SPLIT1["拆分退货参数 2 件"]
    B --> SPLIT2["拆分退货参数 3 件"]
    SPLIT1 --> RET1["创建 180602 退货单 A"]
    SPLIT2 --> RET2["创建 180602 退货单 B"]
    RET1 --> FINISH["CHR 状态 finish"]
    RET2 --> FINISH

拆分计算:

某原出库明细剩余可退 = abs(原出库 qty) - 已关联该出库明细的退货数量
本次分配数量 = min(申请剩余数量, 当前原出库明细剩余可退数量)

9.5 退货通过流程

DGJ/OPS 入口:scm/InvCu::channelAftersalePass()。

sequenceDiagram
    participant OPS as DGJ/OPS
    participant A as BaseAftersale
    participant OUT as 原 180601 出库
    participant CU as InvCuService
    participant RET as 180602 退货单
    participant DB as 渠道售后表
    participant MQ as dgj_channel

    OPS->>A: refundNo + 售后行 + 入库仓位
    A->>DB: 校验当前状态 pass
    A->>A: 校验售后明细
    A->>OUT: 查询原出库及已退数量
    A->>A: 按原出库单拆分数量
    A->>OPS: pushChannelRefundAuditToOps(pass)
    loop 每个原出库单
        A->>CU: submitOrderV2(transType=180602)
        CU->>RET: 创建关联原单的退货入库
    end
    A->>DB: 状态 finish + finish_time
    A->>MQ: channel_refund_action(pass, billNo)

构造 180602 时关键关联:

  • srcOrderId/srcOrderNo:原大客户出库主单;
  • srcOrderEntryId:原大客户出库明细;
  • srcChannelOrder:CHR;
  • qty:当前拆分到这张原出库单的 splitQty;
  • 仓库、货位来自 OPS 退货请求。

9.6 拒绝退货

refund_reject() 只允许当前状态为 pass 且售后明细存在:

  1. 推送拒绝结果给 OPS。
  2. 事务把售后状态改为 reject,写审核备注和完成时间。
  3. 对每个原渠道订单明细执行 refund_num -= 售后申请数量。
  4. 发送 channel_refund_action,action=reject,billNo 为空。

如果拒绝失败但 refund_num 没有回退,后续新售后会错误提示超过可售后数量。

10. 核心表和关联关系

10.1 表清单

常量物理表用途关键字段
CHANNEL_ORDERt_channel_order渠道订单主表id, scene_code, sid, order_no, source_order_no, status, goods_num, shiped_num
CHANNEL_ORDER_INFOt_channel_order_info渠道订单明细id, chan_order_id, order_no, source_row_no, sku_code, qty, shiped_num, refund_num
CHANNEL_AFTERSALEt_channel_aftersale渠道售后主表refund_no, source_refund_no, order_no, status, total_num, total_amount
CHANNEL_AFTERSALE_INFOt_channel_aftersale_info渠道售后明细refund_id, refund_no, order_row_no, sku_code, qty, price
CHANNEL_ORDER_SIGN_DETAILt_channel_order_sign_detail签收明细及幂等unique_id, signing_no, order_no, order_row_no, sku_code, qty
SCM_BIG_ORDERt_scm_big_order大客户出库和退货主单id, billNo, transType, srcChannelOrder, srcOrderSource
SCM_BIG_ORDER_INFOt_scm_big_order_info大客户出库和退货明细id, iid, srcOrderId, srcOrderEntryId, sourceInfoRowNo, skuId, qty

10.2 关系图

erDiagram
    CHANNEL_ORDER ||--o{ CHANNEL_ORDER_INFO : "order_no / chan_order_id"
    CHANNEL_ORDER ||--o{ CHANNEL_AFTERSALE : "order_no"
    CHANNEL_AFTERSALE ||--o{ CHANNEL_AFTERSALE_INFO : "refund_id / refund_no"
    CHANNEL_ORDER_INFO ||--o{ CHANNEL_AFTERSALE_INFO : "order_row_no"
    CHANNEL_ORDER_INFO ||--o{ BIG_ORDER_INFO : "srcOrderEntryId"
    BIG_ORDER ||--o{ BIG_ORDER_INFO : "iid"
    BIG_ORDER_INFO ||--o{ CHANNEL_SIGN_DETAIL : "order_row_no"

    CHANNEL_ORDER {
      string order_no
      string source_order_no
      string status
      int shiped_num
    }
    CHANNEL_ORDER_INFO {
      int id
      string source_row_no
      int qty
      int shiped_num
      int refund_num
    }
    CHANNEL_AFTERSALE {
      string refund_no
      string source_refund_no
      string status
    }
    BIG_ORDER {
      string billNo
      int transType
      string srcChannelOrder
    }

10.3 三组数量必须守恒

渠道明细:0 <= shiped_num <= qty
渠道明细:0 <= refund_num <= shiped_num
售后拆分:所有 splitQty 之和 = 本次申请 qty

数据库只记录过程结果,真正判断库存事实时还要联查 180601/180602 大客户明细和库存流水。

11. MQ 消息地图

所有渠道消息目标:dgj_channel。

routing key生产位置主要参数消费方法消费结果
channel_order_manual_createFC/CZG 人工开单后sid, scene_code, channel_order_nochannelOrderManualCreate()推送人工订单到 OPS;失败返回 NACK
channel_order_deliveryBaseOrder::delivery() 提交后orderNo,实际为大客户 billNochannelOrderDelivery()校验 180601/180602 和渠道来源;180601 推送 OPS
channel_refund_action退货通过/拒绝后refundNo, billNo, actionchannelRefundAction()推送售后动作到 OPS
channel_order_approveFC/CZG 审核后sid, order_no, action, scene_codechannelOrderApprove()尝试按服务站设置自动发货
flowchart LR
    P1["人工开单"] --> K1["channel_order_manual_create"]
    P2["发货成功"] --> K2["channel_order_delivery"]
    P3["退货通过/拒绝"] --> K3["channel_refund_action"]
    P4["渠道审核"] --> K4["channel_order_approve"]

    K1 --> C["ChannelOrderNotify"]
    K2 --> C
    K3 --> C
    K4 --> C

    C --> O1["推送 OPS"]
    C --> O2["触发 FC/CZG 自动发货"]

消费者统一注册 MqEventHandleSer::handleBefore/handleAfter。排查重复消费或处理历史时,需要同时看 MQ 事件处理记录和各回调业务日志。

11.1 ACK/NACK 语义要点

  • 人工开单推送 OPS 异常时返回 NACK,有机会按 MQ 策略重试。
  • 发货消息缺 orderNo、找不到大客户单、交易类型或来源不匹配时直接 ACK 丢弃。
  • 自动发货异常被捕获后返回 ACK,不会依靠 MQ 重试。
  • 退货动作参数不完整时直接 ACK 丢弃。

所以“MQ 消费成功”只能证明消息被确认,不代表自动发货或 OPS 推送一定完成,必须结合业务日志和落表事实判断。

12. 全车件模块

12.1 正确理解全车件边界

application/controllers/allcarpart/Controller.php 的注释已经明确:继承该控制器即可自动转发。它不是在 DGJ 内重新实现一套完整采购、销售、库存和资金领域,而是一个带 DGJ 登录态的全车件访问门面。

sequenceDiagram
    participant UI as DGJ 全车件页面
    participant C as allcarpart Controller
    participant ST as StationSer
    participant M as MICRO_URL 微服务

    UI->>C: /allcarpart/{module}/{resource}/{action}
    C->>ST: enableAllCarPart(sid)
    alt 服务站未开通
        C-->>UI: status=500 服务站未开通全车件功能
    else 已开通
        C->>C: 解析 JSON 请求
        C->>M: POST 原 URI,Headers 带 sid/uid/user_name
        M-->>C: JSON 结果
        C-->>UI: 原样输出或包装结果
    end

公共转发行为:

  • 先调用 StationSer::enableAllCarPart(sid) 检查服务站是否开通;
  • 从 DGJ 登录态获取 sid、uid、用户名称;
  • 通过 Client 向 MICRO_URL + uri_string() 发 JSON POST;
  • 请求头带 sid、uid、user_name;
  • 异常统一转换成 JSON code/message。

12.2 路由和业务域

业务域代表控制器代表显式路由主要能力
商品goods/GoodSearch.php, goods/Goods.phpallcarpart/goods/good-search/*商品搜索与查询
采购purchase/Order.php, PoReturnOrder.php, Rfq.phppurchase/po-return-order/*, purchase/rfq/inquiry询价、采购、采购退货
销售sale/SaOrder.php, SaInvoiceOrder.php, SaReturnOrder.phpsale/sa-order/*, sale/sa-invoice-order/*销售订单、出库、退货
库存storage/Inventory.php, Pd.php, StorageArea*.phpstorage/inventory/*, storage/pd/import库存、仓库、货位、盘点
资金fund/PaymentOrder.php, Account.phpfund/payment-order/*, fund/account/*付款单和账户

兜底路由:

allcarpart/(:any)/(:any)/(:any)
  -> allcarpart/$1/$2/index/$3

带中划线、导入、导出、打印等不适合默认映射的接口,在 application/config/routes.php 中单独声明。

12.3 本地增强而不是纯转发的场景

场景DGJ 本地处理微服务处理
销售订单导入上传并解析 Excel,限制不超过 500 行接收结构化商品,校验并返回结果
销售订单打印请求微服务获取订单数据,再补 DGJ 客户、门店和系统打印配置返回订单和明细
采购退货导入本地上传、解析和错误文件处理接收解析后的业务数据
库存/盘点导入导出本地文件、模板或导出流处理查询、校验或保存业务数据
询价 Rfq::inquiry()补 platform=1 和 sid通过 AllCarPartProvider 完成询货

因此全车件问题要先判断是在:

  1. DGJ 路由/登录态/功能开通层;
  2. DGJ 文件解析、打印或导出层;
  3. MICRO_URL 网络调用层;
  4. 全车件微服务业务层。

13. 外部依赖与配置

13.1 Apollo 配置键

配置键用途缺失时当前行为
dkh.max_price_rateFC/CZG 人工开单最高价比例当前场景回退 1.0
dkh.merchant_map场景映射 OPS 商家编码当前场景返回 null,后续询价/开单可能失败
dkh.max_submit_row人工开单最大明细行数回退 50
dkh.once_delivery_merchant必须整单一次发完的渠道列表非数组时回退空数组

不要在文档中保存配置值;应在配置中心按环境核对键是否存在、JSON 是否合法、场景是否覆盖。

13.2 其他依赖

依赖用途典型失败现象
ContactSershopId + sceneCode 映射服务站和客户所有渠道 API 在构造阶段失败
服务站 Mongo 物料商品、库存、商品名称和 invId商品被跳过、创建提示物料不存在、自动发货库存不足
CuOrderMaterielSer服务站成本价、主体创建或发货提示未设置成本价
OPS/OrderCenter Provider大客户结算价、人工订单推送人工开单或 OPS 显示异常
InvCuService180601/180602 大客户单据和库存发货/退货失败或无 billNo
RabbitMQ审核、发货、退货、人工开单事件订单落表但 OPS/自动发货没有动作
MICRO_URL全车件微服务地址全车件统一转发异常

14. 常用排查 SQL

以下 SQL 仅用于只读排查。先替换业务键,不要直接在生产修改数据。

14.1 按外部订单或 CHS 查主单

SELECT id, scene_code, sid, contact_id, source_shop_id,
       order_no, source_order_no, status,
       goods_num, shiped_num, total_amount,
       submit_time, finish_time, create_time
FROM t_channel_order
WHERE order_no = 'CHS...'
   OR source_order_no = '外部订单号'
ORDER BY id DESC;

14.2 查渠道明细数量是否守恒

SELECT id, order_no, source_row_no, sku_code, inv_id,
       qty, shiped_num, refund_num,
       sale_price, cost_price, sale_amount
FROM t_channel_order_info
WHERE order_no = 'CHS...'
ORDER BY id;

重点检查:

shiped_num <= qty
refund_num <= shiped_num
sale_amount = qty * sale_price

14.3 查 CHS 关联的大客户出库

SELECT o.id, o.billNo, o.transType, o.srcChannelOrder,
       i.id AS out_info_id, i.srcOrderId, i.srcOrderEntryId,
       i.sourceInfoRowNo, i.skuId, i.qty
FROM t_scm_big_order o
JOIN t_scm_big_order_info i ON i.iid = o.id
WHERE o.srcChannelOrder = 'CHS...'
ORDER BY o.id, i.id;

若实际表按 sid 分库或存在环境差异,应先用模型 SQL 或数据库连接确认真实库表位置。

14.4 查售后申请和明细

SELECT id, scene_code, sid, order_no, source_order_no,
       refund_no, source_refund_no, status,
       total_num, total_amount, finish_time, create_time
FROM t_channel_aftersale
WHERE refund_no = 'CHR...'
   OR source_refund_no = '外部售后号'
ORDER BY id DESC;

SELECT id, refund_id, refund_no, order_row_no,
       source_order_row_no, sku_code, qty, price, amount
FROM t_channel_aftersale_info
WHERE refund_no = 'CHR...'
ORDER BY id;

14.5 查签收幂等记录

SELECT unique_id, scene_code, sid, signing_no,
       signing_person, signing_time,
       order_no, order_row_no, sku_code, qty
FROM t_channel_order_sign_detail
WHERE signing_no = '签收批次号'
ORDER BY order_row_no;

15. 日志与代码检索

15.1 本地快速定位

rg -n "平台订单创建异常|平台订单发货服务异常|平台退货确认入库异常|大客户自动发货异常" application
rg -n "channel_order_manual_create|channel_order_delivery|channel_refund_action|channel_order_approve" application
rg -n "source_order_no|srcChannelOrder|srcOrderEntryId|sourceInfoRowNo" application/Services/Channel application/models
rg -n "allcarpart/|MICRO_URL|enableAllCarPart" application/config application/controllers/allcarpart

15.2 业务日志分类

链路Logger 分类/关键字
外部渠道 APIinner/api,搜索“平台订单创建异常”“退货申请异常”
渠道 Service对应 Channel Service logger,搜索 CHS/CHR/外部单号
人工开单 MQtasks/channelOrderManualCreate
发货 MQtasks/channelOrderDelivery
售后 MQtasks/channelRefundAction
审核自动发货tasks/channelOrderApprove,关键字“大客户自动发货异常”

排查顺序建议:业务表事实 -> 大客户单据事实 -> 库存事实 -> MQ 处理 -> OPS/外部回传,不要只凭接口返回判断。

16. 按现象排查

16.1 外部渠道创建失败:绑定异常

flowchart TD
    A["服务站和修理厂绑定信息异常"] --> B{"sceneCode 是否 dcd/fc/czg"}
    B -->|否| C["修正场景码"]
    B -->|是| D{"shopId 是否正确"}
    D -->|否| E["修正渠道客户标识"]
    D -->|是| F["检查 ContactSer 渠道关系和 300 秒缓存"]
    F --> G["确认返回 stationInfo/contactInfo"]

不要先补订单数据。绑定不成立时控制器不会进入创建事务。

16.2 渠道说下单成功,DGJ 查不到

  1. 用外部 orderNo 查 t_channel_order.source_order_no。
  2. 查请求是否实际到达当前环境和节点。
  3. 查 sceneCode/shopId 映射出的 sid 是否是业务人员查看的服务站。
  4. 查创建日志中的物料、成本价、小计错误。
  5. 若存在主单但无明细,检查事务和表引擎;正常逻辑应一起提交或一起回滚。

16.3 FC/CZG 一直待审核

  1. 确认状态确实为 created。
  2. 检查渠道是否调用 order_approve。
  3. 检查 saleOrderNo、shopId、sceneCode 是否与原单一致。
  4. 检查通过时新的 sourceOrderNo 是否已被同场景占用。
  5. 查审核接口日志和 channel_order_approve 消息。

16.4 审核通过但没有自动发货

  1. 确认订单已经是 wait_ship。
  2. 查 channel_order_approve 是否生产和消费。
  3. 核对 fc_auto_send/czg_auto_send 服务站设置。
  4. 检查默认管理账号和默认仓库。
  5. 检查每个 SKU 在返回的第一个仓位中是否有足够库存。
  6. 查“大客户自动发货异常”;消费者会 ACK,不要只看队列无积压。
  7. 自动发货失败后按业务规则人工发货。

16.5 发货提示超出待发数量

  1. 查渠道明细 qty 和 shiped_num。
  2. 查请求 rowNo 是否属于当前 CHS。
  3. 查是否有上次发货已创建 180601,但前端仍提交旧数量。
  4. 检查是否配置为一次性发货渠道。
  5. 对照所有关联大客户出库明细数量,确认渠道累计值是否一致。

16.6 发货接口失败但库存已变化

这是高风险不一致场景:

  1. 按返回或日志中的 billNo 查 t_scm_big_order。
  2. 查库存流水和实时库存是否已经发生出库。
  3. 查渠道 shiped_num/status 是否提交。
  4. 查事务边界内 InvCuService::submitOrderV2() 是否使用同一数据库连接,外部副作用是否可回滚。
  5. 在确认事实前不要重复发货;重复调用可能再次扣库存。

16.7 退货提示没有可退商品

  1. 查 CHR 明细的 order_row_no。
  2. 用该 ID 查 180601 明细的 srcOrderEntryId。
  3. 确认原出库单真实存在且来源关系完整。
  4. 汇总该原出库明细已关联的 180602 数量。
  5. 计算剩余可退是否大于零。
  6. 检查历史出库是否缺 srcOrderEntryId/sourceInfoRowNo,这类老数据可能无法自动拆分。

16.8 渠道/OPS 没收到通知

  1. 先确认订单、出库或退货事务已成功。
  2. 根据业务动作确认 routing key。
  3. 查生产日志和 MQ 事件记录。
  4. 查 ChannelOrderNotify 对应回调日志。
  5. 判断是 NACK 可重试,还是业务校验后 ACK 丢弃。
  6. 按业务主键补偿消息,不要重复创建业务单据。

16.9 全车件统一返回未开通

  1. 确认当前登录服务站 sid。
  2. 检查 StationSer::enableAllCarPart(sid) 的开通事实。
  3. 确认不是登录到了其他站点。
  4. 开通后重新请求,不需要修改全车件业务数据。

16.10 全车件 404 或转发异常

  1. 对照 routes.php 是否有中划线路由或特殊动作映射。
  2. 确认兜底路由的模块、控制器、动作三段是否正确。
  3. 查控制器是否继承 allcarpart\Controller。
  4. 检查 MICRO_URL 和网络连通性。
  5. 检查下游接口是否接受 JSON POST 和当前 URI。
  6. 导入、导出、打印问题继续检查 DGJ 本地文件处理,不要只查微服务。

17. 当前代码风险与待确认项

以下是当前代码事实或静态审查风险,不应在没有回归验证时顺手修改公共逻辑。

17.1 售后明细单号判断疑似存在运算优先级问题

BaseAftersale::checkRefundDetails() 当前条件为:

if (!$dbAfterInfo['refund_no'] != $item['refundNo'])

这不是直观的“不相等”写法,! 会先作用于字符串再比较。需要补测试确认实际行为,预期逻辑大概率应是比较数据库 refund_no 与请求 refundNo 是否一致。

17.2 OPS 退货通过入口可能掩盖异常

scm/InvCu::channelAftersalePass() 捕获异常后构造了错误 $result,但最终响应固定使用 code=0 和请求明细 ID,没有返回该错误结果。调用方可能看到成功,而 Service 实际已经回滚。

排查退货通过时必须查 CHR.status、180602 单据和日志,不能只相信接口 code。

17.3 查询退货入库接口是空实现

inner/channel/Aftersale::get_return_stock_in() 当前直接返回空成功结果,没有查询退货入库单。外部渠道若依赖该接口获得入库状态,需要先确认真实合同,不能把空数组解释为“已完成”。

17.4 创建查重范围可能过宽

创建按 source_order_no 单字段查重,没有附加场景和客户。需确认外部订单号是否全渠道全局唯一,并检查数据库唯一索引。

17.5 发货金额字段需要回归确认

buildBigOrderDeliverData() 的总金额使用请求明细 price,而单行 amount 使用数据库明细的 price 键;渠道明细常见字段名是 sale_price。需要通过现有发货回归和实际数组结构确认是否由模型别名补齐,避免金额为零或字段未定义。

17.6 自动发货消息异常不会重试

审核消费者捕获异常后仍返回 ACK。这是当前容错策略,不代表业务自动恢复;应有人工待发列表或补偿入口兜底。

17.7 全车件错误码并不完全统一

公共转发异常返回 code/message,未开通分支直接输出 status/message,个别导入接口又使用自身结构。前端和联调文档不能假设所有全车件接口只有一种响应 envelope。

18. 改动影响面

修改位置直接影响必须回归
BaseChannel / 绑定解析所有渠道商品、订单、售后三个场景、有效/无效 shopId、缓存
BaseOrder::formatRequestToOrderEntry()三渠道订单主数据编号、客户地址、金额、初始状态差异
ChannelOrderTraitFC/CZG人工开单、审核、自动发货、复制订单
DcdOrderServiceDCD直接待发、成本价、创建幂等
BaseOrder::delivery()三渠道和大客户出库部分/全部发货、库存、金额、MQ、重复请求
BaseAftersale三渠道售后和大客户退货申请占用、拆单、入库、拒绝回退、MQ
InvCuService::submitOrderV2()所有大客户出入库普通大客户业务和渠道业务都要回归
MqSer / ChannelOrderNotifyOPS 通知和自动发货四种 routing key、ACK/NACK、幂等
routes.php / allcarpart Controller全车件所有页面显式路由、兜底路由、开通校验、转发头

19. 回归测试清单

19.1 商品和绑定

  • DCD/FC/CZG 有效绑定能查询商品列表和详情。
  • 无效 sceneCode、缺 shopId、关系不存在返回清晰错误。
  • 商品列表和详情库存口径符合预期。
  • 超过最大 SKU 数时截断行为可接受。

19.2 创建

  • DCD 创建后为 wait_ship。
  • FC/CZG 创建后为 created。
  • 同一来源订单重复请求返回同一 CHS,不重复明细。
  • 商品不存在、数量非正、单价过低、小计错误、成本价缺失均失败且不留半单。
  • 主单汇总金额、成本和价差等于明细合计。

19.3 人工开单与审核

  • 仅 FC/CZG 可人工开单;DCD 明确拒绝。
  • 行数上限、重复 SKU、结算价、最高价、库存校验生效。
  • 审核通过 created -> wait_ship,拒绝 created -> cancel。
  • 重复审核被拒绝。
  • 审核消息能触发已开启服务站自动发货,未开启时订单仍保留待发。

19.4 发货

  • 待发订单可一次全部发货并进入 finish。
  • 非一次发完渠道可部分发货并进入 part_ship,累计发完进入 finish。
  • 一次发完渠道拒绝部分行或部分数量。
  • 发货创建 180601,仓库和货位库存正确减少。
  • 渠道主明细 shiped_num 与大客户出库数量一致。
  • submitOrderV2() 无 billNo 时渠道数据不提交。
  • 发货 MQ 能在 OPS 侧找到对应大客户单。

19.5 签收

  • SKU、数量、总数完全一致时写签收明细。
  • SKU 不一致、数量不一致、行不存在时拒绝。
  • 同一签收批次重复回调只保留一条唯一记录。

19.6 售后

  • 申请数量不超过已发且未占用数量。
  • 相同 CHS + sourceRefundNo 重复请求返回同一 CHR。
  • 申请后 refund_num 正确增加。
  • 一张渠道明细跨两个出库时,退货能拆成对应的 180602。
  • 拆分数量总和等于申请数量,不超原出库可退数量。
  • 拒绝后 refund_num 完整回退。
  • Service 异常时 OPS 接口不能被误判为业务成功。

19.7 MQ

  • 四种 routing key 都能被消费者识别。
  • 缺参数、业务单不存在、重复消息有明确 ACK/NACK 行为。
  • 人工开单推 OPS 失败可重试。
  • 自动发货失败可人工补偿,且不会重复扣库存。

19.8 全车件

  • 未开通服务站被功能开通校验拦截。
  • 已开通服务站能通过显式路由和兜底路由转发。
  • 下游收到正确 sid/uid/user_name。
  • 销售订单导入超过 500 行被拒绝,合法文件可返回成功和错误数据。
  • 打印能补齐客户、门店、系统配置并支持现有打印方式。
  • 微服务不可用时前端获得可识别错误,不出现 HTML 500 页面。

20. 代码证据索引

结论证据文件
场景、来源类型、Apollo 键application/Services/Channel/SceneConst.php
绑定和场景工厂application/controllers/inner/channel/BaseChannel.php
外部创建、审核、签收入口application/controllers/inner/channel/Order.php
商品查询入口和数据组装application/controllers/inner/channel/Goods.php, application/Services/Channel/BaseGoods.php
DCD 创建后直接待发application/Services/Channel/Dcd/DcdOrderService.php
FC/CZG 创建、审核、人工开单、自动发货application/Services/Channel/Traits/ChannelOrderTrait.php
发货、签收和大客户出库转换application/Services/Channel/BaseOrder.php
售后申请、拆原出库、退货通过/拒绝application/Services/Channel/BaseAftersale.php
订单和售后状态application/KzData/Enums/ChannelOrderEnums.php, ChannelAftersaleEnums.php
OPS 操作入口application/controllers/scm/InvCu.php
MQ 生产和消费application/Services/Mq/MqSer.php, application/controllers/tasks/ChannelOrderNotify.php
物理表名application/config/tables.php
全车件转发门面application/controllers/allcarpart/Controller.php
全车件显式和兜底路由application/config/routes.php
全车件导入和打印示例application/controllers/allcarpart/sale/SaOrder.php

21. 仍需环境确认

以下内容无法仅凭仓库静态代码证明,联调或上线前必须补证据:

  1. 生产网关对 inner/channel/* 暴露的完整 URL、HTTP 方法、鉴权和签名规则。
  2. 三个渠道的外部订单号是否真正全局唯一。
  3. 各环境四个 dkh.* Apollo 键的实际覆盖情况。
  4. t_channel_order.source_order_no、签收 unique_id 等字段的真实唯一索引。
  5. submitOrderV2() 的数据库事务边界能否覆盖全部库存副作用。
  6. OPS 对四类 MQ 的最终接收接口、重试和告警机制。
  7. MICRO_URL 对应服务、超时、重试和熔断配置。
  8. get_return_stock_in() 是否已经被外部渠道调用以及预期返回合同。

完成这些确认后,应把结果更新回本文,而不是只留在聊天或临时联调记录里。

请求-日志-数据变更追踪卡

多入口请求链路

场景调用方与入口请求载荷/上下文Controller/ConsumerService/Provider汇合点最终业务事实
渠道创建订单大客户/渠道内部接口 /inner/channel/*channel、外部订单号、sid、商品、收货信息controllers/inner/channel/Order.phpChannel/BaseOrder.phpchannel + source_order_no渠道单主明细创建并转换为内部履约单
渠道发货/动作外部渠道通知或 OPS 操作外部单号、发货数量、物流、动作类型Channel Order/OPS InvCuBaseOrder渠道单号 + 内部出库单号发货累计、物流和渠道状态推进
售后申请/审核渠道售后接口或 OPS原订单、商品、申请数量、原因、审核结果Channel Aftersale ControllerBaseAftersale.php外部售后单号 + 原出库单售后主明细、退货入库和退款结果
渠道 MQDEST_DGJ_CHANNEL手工创建、发货、退款、审核等事件tasks/ChannelOrderNotify.phpChannel Service + MqSer事件业务键 + 渠道单号异步动作落库并将结果发送渠道
全车件代理/allcarpart/* 显式/兜底路由路由模块、接口参数、来源单号allcarpart/Controller.php 及子 ControllerMICRO_URL 对应微服务路径 + 来源单号请求转发到全车件服务或处理导入/打印

日志证据矩阵

链路段日志来源可检索锚点成功信号失败信号与下一段关联方式
渠道接入Inner API/Channel Controllerchannel、source_order_no、URI、request_id返回内部渠道单号签名/参数失败、来源单重复外部单号查 CHANNEL_ORDER
内部转换BaseOrder/销售出库 Service渠道单号、内部销售/出库单号关系建立且状态 created -> wait_ship商品映射、库存、地址或事务失败关系字段串联渠道与内部单据
发货签收BaseOrder + Channel MQ渠道单、出库单、物流单、消息 IDshipped_num 累计且状态推进超发、重复消息、物流回传失败SKU 明细 + CHANNEL_ORDER_SIGN_DETAIL
售后BaseAftersale售后单号、原单、SKU、审核动作申请/审核/入库/退款节点完整超退、原单不存在、退款失败售后单关联原渠道单和内部退货单
全车件转发allcarpart/Controller/HTTP Provider原始路径、来源单号、下游返回码下游 2xx/业务成功码超时、非成功码、兜底路由错误request_id + 来源单号在两端检索

环节数据变更台账

步骤代码位置事务读取事实写入表/缓存/MQ字段或数量变化回查证据
接收订单inner/channel/Order.php -> BaseOrder渠道订单事务来源单唯一性、站点、商品映射CHANNEL_ORDER、CHANNEL_ORDER_INFO主明细 insert;status=created;订购数量/金额写入channel + 外部单号 + SKU
转内部履约BaseOrder::submitOrderV2 等跨渠道/销售事务边界需核对渠道明细、可售库存、地址销售/出库相关表、渠道关系字段写内部单号;渠道状态 -> wait_ship渠道单与内部单双向回查
发货BaseOrder 发货方法发货事务order_num - shipped_num - cancel_num渠道明细、物流/签收表、渠道 MQshipped_num: old -> old+n;状态 wait_ship -> part_ship/finish每 SKU 数量等式、消息 ACK
售后申请BaseAftersale售后事务原出库与可退数量CHANNEL_AFTERSALE、CHANNEL_AFTERSALE_INFOinsert;申请量写入;状态初始化售后单号、原单、SKU
售后通过/退货BaseAftersale + 库存/退款 Service本地事务与外部退款异步边界已审核、已退、可入库量售后表、内部退货/库存、退款 MQrefund_num/return_num: old -> old+n;库存按退货增加;状态 -> 完成原单/售后单/退货单/支付单闭环
签收回传渠道签收处理消费事务物流状态、unique_idCHANNEL_ORDER_SIGN_DETAIL、订单状态/MQ签收记录 insert;最终状态按全部履约推进unique_id 幂等、物流单、渠道状态

子模块级独立追踪

子模块追踪:channel-goods 渠道商品查询

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
查询/inner/channel/Goodsrequest ID、channel、external station/customer、SKU/queryapplication/controllers/inner/channel/Goods.php -> Channel Goods Service渠道绑定、商品映射、价格、可售库存和范围只读,不写业务表;外部商品请求 -> 渠道可售结果request ID + channel + SKU + sid无映射/不可售返回明确业务码;不为修页面结果直接改库存或商品状态
外部回查渠道方核对商品external request ID、channel SKU、DGJ SKUapplication/Services/Channel/BaseService.php 及渠道子类本地响应快照和渠道合同DB 不变;仅对比两端字段/单位两端 request ID + channel SKU/DGJ SKU字段合同差异修适配层并回归所有渠道,不污染基础商品

子模块追踪:channel-create 渠道订单创建

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
接单/inner/channel/Orderrequest ID、channel、source_order_no、sid、商品、地址application/controllers/inner/channel/Order.php -> Channel/BaseOrder.php外部单唯一性、渠道绑定、商品和地址渠道事务 insert CHANNEL_ORDER/INFO;status 无 -> created,order_num/amount 写入request ID + channel + source order + local channel order重复来源返回/查询原单;参数失败零写入,不能用随机 message ID 代替业务幂等
转内部单接单后提交channel order、内部 source/type、SKU/qtyapplication/Services/Channel/BaseOrder.php::submitOrderV2 -> 销售/大客户 Service渠道明细、价格库存、服务类型跨域事务 insert 内部销售/大客户事实单,关系无 -> internal bill;status -> wait_shipsource order + channel order + internal bill部分成功按关系补缺失段;禁止重建渠道意图单或已存在事实单

子模块追踪:channel-audit 人工代建与渠道审核

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
人工代建OPS scm/InvCurequest ID、operator、channel/source order、客户商品application/controllers/scm/InvCu.php -> InvCuService/Channel Service渠道配置、客户绑定、来源唯一性事务 insert 渠道/大客户单和操作记录;status 无 -> created/pendingrequest ID + operator + source/local bill页面超时先按 source order 查重;权限失败零写入
审核OPS 或渠道 MQ approve/actionmessage ID、channel order、action、reasonapplication/controllers/tasks/ChannelOrderNotify.php -> BaseOrder当前状态、审核人、自动发货条件单消息事务 status created/pending -> approved/rejected/wait_shipmessage ID + channel/source order + action并发/重复审核 0 副作用;自动发货失败保留已审核事实并进入独立补偿

子模块追踪:channel-ship 渠道发货与库存出库

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
发货渠道发货 API、OPS 或 MQrequest ID/message ID、channel/source/internal bill、SKU、qtyapplication/Services/Channel/BaseOrder.php -> 大客户/销售出库 Service -> InventorySer可发=order-shipped-canceled、内部事实单和库存发货事务 shipped_num: old -> old+n;内部出库 insert;库存实时 old -> old-n三种单号 + SKU + transType + message ID超发/重复拒绝;出库库存部分成功按内部出库单补断点,不重做渠道发货
外部回写本地 commit 后source order、shipment/logistics no、routing keyapplication/Services/Mq/MqSer.php / Channel Provider已提交发货和渠道目标本地 DB 不变;外部状态 wait_ship -> part_ship/finishsource order + shipment + message ID推送失败只补外部通知;外部已收 ACK 丢失时重发必须幂等

子模块追踪:channel-sign 渠道签收

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
签收事件渠道/配送签收回调message ID、source order、shipment、unique_id、sign qty/timeapplication/Services/Channel/BaseOrder.php 签收处理发货明细、已有 unique_id、当前状态消费事务 insert CHANNEL_ORDER_SIGN_DETAIL;signed qty old -> old+n,全部签收 status -> finishunique_id + source/channel order + shipmentunique_id 重复为 0 变化;签收量超发拒绝并告警
通知签收 commit 后source order、sign record、message IDapplication/Services/Mq/MqSer.php本地最终签收事实DB 不变;渠道/订单中心派生状态 old -> signedsign unique_id + routing key通知失败只补签收事件,不重复写签收明细

子模块追踪:channel-aftersale 渠道售后申请与审核

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
申请/inner/channel/Aftersalerequest ID、source aftersale/order、SKU、qty、reasonapplication/controllers/inner/channel/Aftersale.php -> BaseAftersale.php原发货、已申请/已退量、来源唯一性售后事务 insert CHANNEL_AFTERSALE/INFO;apply/refund num 0 -> n,status 无 -> pendingrequest ID + source aftersale + original source order超退/重复来源零写入;响应未知按 source aftersale 查原申请
审核OPS/ChannelNotify approve/rejectmessage ID、aftersale order、action、operatorapplication/controllers/tasks/ChannelOrderNotify.php -> BaseAftersale当前售后态、原单可退量单消息事务 pending -> approved/rejected;拒绝释放占用量 old -> old-nmessage ID + aftersale/original order + action重复审核 0 变化;审核通过后退货单创建失败作为下一模块补偿,不回退审批事实

子模块追踪:channel-return 渠道退货入库与退款

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
退货入库服务站确认收货request ID、aftersale/original invoice、SKU、warehouse/location、qtyapplication/Services/Channel/BaseAftersale.php -> 销退 Service -> InventorySerapproved qty、已收量、原出库明细退货事务 return qty old -> old+n;库存实时 old -> old+n,写销售退货流水aftersale + internal return bill + transType + SKU多原出库拆单分别幂等;库存已加状态未写只补状态/关系
退款完成支付/渠道退款事件refund/pay ID、aftersale、amount、message IDapplication/Services/Channel/BaseAftersale.php -> Payment/Mq Service原支付、已退金额、退货入库事实财务事务 refunded old -> old+n;售后 status -> finish,外部通知异步refund/pay ID + aftersale/source order资金与库存分段补偿;重复退款消息不得重复入账或加库存

子模块追踪:allcarpart 全车件路由、代理与本地能力

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
路由代理/allcarpart/* 显式或兜底路由request ID、原 path、用户/sid、source order、payloadapplication/config/routes.php -> application/controllers/allcarpart/Controller.php -> MICRO_URL路由分类、身份上下文、转发参数纯代理时本地 DB 不变;外部系统状态由无 -> 受理request ID + path + source order + external request IDHTTP 200 仍核业务码;超时用外部 ID 查对方,避免重复提交
本地能力导入、打印、导出等全车件子 Controllerrequest ID、file/task/order IDapplication/controllers/allcarpart/sale/SaOrder.php 等 -> 本地 Service文件、模板、本地订单/打印数据按具体事务写导入任务/业务表或只读生成文件,不能一概视为代理request/task/order ID + Controller 方法区分本地失败与微服务失败;只补对应段,文件重放按稳定业务键去重