1. 模块定位
这个模块把“修理厂在移动端找货、询价、下单、收货、补货、退货”接到 DGJ2 的商品、价格、销售、库存和财务体系中,同时承接微信群、腾讯 IM、钉钉/API 等机器人会话入口。
它不是一条单一链路,而是四组相互关联的业务:
- 移动商城/E站商品:按客户、服务站、门店、仓库、价格和场景返回商品。
- E站订单:把小程序、APP、IM、活动或机器人订单转换成 DGJ2 销售订单。
- 微仓履约:补货、派送、确认、上架和移动退货,本质上会联动调拨和库存。
- 智能询价/RobotV2:解析 VIN、车型、关键词、图片和语音,查询商品并可继续转报价或订单。
1.1 业务全景
flowchart LR
U[修理厂用户] --> MP[E站小程序]
U --> APP[E站 APP/IM]
U --> WX[微信群机器人]
MP --> GOODS[移动商城商品服务]
APP --> GOODS
WX --> ROBOT[RobotV2/智能询价]
ROBOT --> GOODS
GOODS --> P[客户价格/报价规则]
GOODS --> I[服务站仓/微仓/供给库存]
MP --> ORDER[E站订单入口]
APP --> ORDER
ROBOT --> ORDER
ORDER --> SA[DGJ2 销售订单]
SA --> TAKE[接单/后续出库]
TAKE --> INV[库存与配送]
INV --> MQ[SAAS/消息通知]
W[微仓运营] --> SUPPLY[补货单]
SUPPLY --> TF[调拨出库/入库]
2. 先统一关键概念
| 概念 | 业务含义 | 不能混淆的对象 |
|---|---|---|
contact_id | 修理厂/客户 ID | 不是移动用户 ID,也不是服务站 sid |
station_id / sid | 接单和履约服务站 | 请求进入 saCreate2() 后会统一为 sid |
order_id | SAAS/E站源订单 ID | DGJ2 新销售订单 ID 另行生成 |
order_no | 源订单号,同时写入 DGJ2 billNo/srcOrderNo | 幂等校验按它查询已有销售单 |
order_scene | E站业务场景 | 决定销售来源、消息类型、支付账户和是否记录 SAAS 关系 |
payMode | 上游支付模式 | CHARGE/WEXPAY/UnionPay 会转换为 DGJ 内部支付语义 |
storage_id | 微仓或指定履约仓 | 普通 E站订单不一定直接从该仓扣库存 |
srcOrderEntryId | 上游订单明细 ID | 用于跨系统明细关联 |
conversation_id | RobotV2 正式会话 ID | AI 异步识别还可能先使用预会话 ID |
trace_id | RobotV2 单次处理追踪 ID | 不是业务订单号 |
2.1 三种“下单后结果”
flowchart TD
A[移动端请求下单] --> B{order_scene}
B -->|E站/APP/活动/机器人| C[创建 DGJ2 销售订单]
C --> D[建立 SAAS 源订单关系]
C --> E[创建接单记录并发消息]
E --> F[后续接单、出库、配送]
B -->|废弃 pick 领料| G[创建销售订单]
G --> H[立即创建出库单]
H --> I[立即写库存流水/收款]
B -->|微仓补货| J[不走 E站销售订单]
J --> K[创建补货单和调拨单]
普通 E站下单成功只证明销售订单落库,不代表已经出库、扣减服务站实时库存或完成收款。
3. 代码入口地图
3.1 对外/内部入口
| 场景 | 入口 | 方法 | 核心下游 |
|---|---|---|---|
| 移动端创建订单 V2 | application/controllers/inner/moveMall/Order.php | createOrder() | SaOrderSer::saCreate2($data) |
| 旧创建/退货混合入口 | 同上 | create() | saCreate() 或 retOrder() |
| 追加订单 | 同上 | appendOrder() | SaOrderSer::saAppend() |
| 创建退货申请 | 同上 | createOrderReturn() | retOrder2() |
| 挂账单退货 | 同上 | createChargeOrderReturn() | returnChargeOrder() |
| 未发货退货 | 同上 | returnOrderNoOut() | 对未出库订单走反向申请 |
| 申请/取消报价 | 同上 | applyOfferOrder/cancelOfferOrder | OfferOrderApplySer |
| 检查是否出库 | 同上 | checkOrderOutStatus() | 按销售单 ID 和 sid 查询 |
| 分仓拆单 | inner/moveMall/BranchStore.php | split() | BranchStoreOrderSplitSer::splitOrder() |
| 商品查询 | inner/moveMall/Material.php | eGoodsList/eSaasGoodsList/searchGoods/... | MoveMallGoodsSer |
| 微信消息回调 | inner/moveMall/Robot.php | receiveMsgCallback() | 旧 RobotInquirySer::onMessageAsync() |
| RobotV2 AI 回调 | 同上 | robotV2AiCallback() | 预会话恢复、RobotFacade |
| 微仓补货管理 | moveMall/MoveSupply.php | 列表、创建、上架 | MoveSupplySer |
| E站确认补货 | inner/moveMall/MsOrder.php | msConfirm() | MoveSupplySer::updateMsStatus() |
| 移动退货上架 | MoveReOd.php 及 Service | 列表、详情、扫码入库 | MoveReOdSer |
| Robot V2 测试 | RobotV2Test.php | 按查询类型和展示策略测试 | RobotV2 各 Service |
3.2 核心 Service
| Service | 职责 |
|---|---|
MoveMallGoodsSer | 商品列表、详情、价格、微仓/服务站仓库存、可售过滤、搜索日志 |
SaOrderSer | E站请求校验、销售主单/明细落库、来源映射、接单和消息 |
BranchStoreOrderSplitSer | 根据库存和门店能力分配订单商品 |
MoveStoSer | 微仓开通、商品维护、价格、补货推荐和领用统计 |
MoveSupplySer | 补货单、调拨出库、派送、确认和上架入库 |
MoveReOdSer | 移动退货查询、扫码和上架入库 |
RobotInquirySer | 旧机器人消息、VIN/关键词查询和报价文本输出 |
RobotV2/RobotFacade | 新版消息适配、Intent、状态机、Reply 和发送器总入口 |
RobotV2/QueryDispatcher | 根据查询场景选择商品查询服务并执行后处理 |
RobotOrderSer | 把机器人选品转换为移动订单请求 |
RobotQuoteConfigSer | 下单数量和报价回复配置 |
4. E站订单场景和销售来源
场景定义在 MoveMallEnums,销售来源定义在 SaOrderEnums。
order_scene | 前端含义 | 挂账来源 | 在线支付来源 |
|---|---|---|---|
move_send | E站维修/马上送 | 10 E站维修 | 15 E站维修微信 |
e_shop | E站商城 | 8 E站商城挂账 | 9 E站商城微信 |
e_activity | E站活动 | 14 活动来源 | 同一活动来源,支付再单独判断 |
e_robot | E站机器人 | 16 AI 助手订单 | 当前映射不按在线支付拆来源 |
e_app_im | E站 APP IM | 17 | 18 |
e_app_shop | E站 APP 商城 | 19 | 20 |
e_app_activity | E站 APP 活动 | 21 | 22 |
e_shop_im | E站商城 IM | 代码注释为已废弃 | 仍出现在分仓支持场景中 |
pick | 微仓领料 | 已废弃 | 旧代码仍保留即时出库分支 |
17-22的具体常量名分别是ORDER_SOURCE_TYPE_APP_IM/_WX、APP_SHOP/_WX、APP_ACT/_WX;数值应以当前SaOrderEnums.php为准。
4.1 支付模式转换
上游 payMode | DGJ 用户支付方式 | 说明 |
|---|---|---|
CHARGE | 0 挂账 | 走 App 默认账户或场景结算配置 |
WEXPAY | 1 微信支付 | 使用巨划算/在线支付账户与结算方式 |
UnionPay | 1 微信支付语义 | 当前枚举明确映射为同一内部在线支付类型 |
saOrderMainFields() 会根据场景和支付方式同时决定:
sourceType;- 结算账户
accId; - 结算方式
wayId; is_pay等支付标志;- 机器人订单的销售员
salesId; - 销售单描述和来源字段。
5. V2 创建销售订单接口
5.1 推荐入口
当前代码确认的直接主链是:
inner/moveMall/Order::createOrder()
-> RequestCheck::checkRepeat(KEY_MOVE_OD_ADD_LOCK, request hash)
-> SaOrderSer::saCreate2($data)
5.2 请求示例
{
"order_scene": "e_app_im",
"order_id": 900001,
"order_no": "EAPP-EXAMPLE-001",
"station_id": 1001,
"contact_id": 2001,
"store_id": 10,
"payMode": "CHARGE",
"totalQty": 2,
"totalAmount": "198.00",
"totalDiscount": "0.00",
"disRate": "1.0000",
"disAmount": "0.00",
"description": "移动端订单",
"vin": "",
"entries": [
{
"srcOrderEntryId": 910001,
"skuId": "SKU-10001",
"qty": 2,
"price": "99.00",
"amount": "198.00",
"disAmount": "198.00",
"discountRate": "1.0000",
"deduction": "0.00",
"isGift": 0,
"carModel": ""
}
]
}
这是按 validform_sa() 和后续字段读取整理的结构示例,不代表所有网关字段都是必填。必须通过联调确认源系统是否还传配送、优惠券或分仓扩展字段。
5.3 入口校验
validform_sa() 已确认执行:
- 从
station_id取得sid。 order_no不能为空。- 按
sid + order_no查询已有销售单,存在即报“重复创建”。 entries不能为空,且必须能提取skuId。- 通过服务站物料缓存批量查询商品,不可用则失败。
- 每行
qty必须大于 0。 - 非赠品
price不能为 0。 - 用商品缓存补齐
invId、单位、车型和指导价。 - 销售数量写入单据前统一转换为负数。
storeDefault优先使用请求store_id,否则使用服务站管理员默认门店。
重复请求还会在 Controller 层经过 RequestCheck::checkRepeat() 的 Redis 防重,但最终业务幂等仍由源订单号查询承担。
6. E站订单落库完整时序
sequenceDiagram
autonumber
participant E as E站/APP/机器人
participant O as inner Order.createOrder
participant S as SaOrderSer.saCreate2
participant DB as 销售订单分表
participant REL as t_scm_sass_order_rel
participant TAKE as SaOrderTakeSer
participant MQ as 消息中心
E->>O: order_scene + order_no + entries
O->>O: Redis请求防重
O->>S: saCreate2(data)
S->>S: validform_sa 校验商品/数量/价格
S->>DB: 开事务写销售主单
S->>REL: 写源 order_id 与 sa_order_id 关系
loop 每条商品
S->>DB: 写销售明细和 srcOrderEntryId
opt 存在 VIN
S->>DB: 写销售明细扩展 VIN
end
end
S->>DB: 提交事务
S->>TAKE: 创建待接单记录
S->>MQ: 按场景发送站内/邮件类通知
S-->>E: DGJ销售单 id、billNo、配送展示信息
6.1 事务内数据
- 销售订单主表;
- 销售订单明细;
- E站/SAAS 源订单关系
t_scm_sass_order_rel; - 有 VIN 时的销售明细扩展数据。
6.2 事务提交后的副作用
SearchLogSer::addSaOrderMsg()记录 VIN 搜索转单;SaOrderTakeSer::create()创建接单记录;MqSer::sendMailToMq()按move_send/e_shop/e_activity/e_robot/e_app_*发送不同消息类型;buildDelivery()计算仓库展示、配送时效和展示配置。
事务提交后副作用失败是否回滚主订单,需要看各调用的异常处理;不能默认所有通知和主表原子一致。
6.3 E站 APP 优惠金额特殊处理
当场景属于 E_APP_SCENES 时,明细的 amount 和 disAmount 都使用请求 row.disAmount。这是为了兼容 APP 已在上游分摊优惠券的口径。对账时不能简单用 price * qty 代替落库金额。
7. 旧 create() 入口的当前风险
当前代码存在如下事实:
public function saCreate($data)
{
return $this->saCreate2();
// 后续旧实现不可达
}
而 saCreate2($data) 参数不是可选。因此:
Order::create()的销售分支调用saCreate($data),按当前代码路径可能触发缺少参数错误。saCreate()后面那套“创建订单后立即出库”的旧实现不可达,不能再作为当前业务事实。- 新接入应使用
createOrder();旧调用方是否仍存在必须通过访问日志或代码仓库全局搜索确认。
这不是本文推测,而是当前工作区的明确代码风险。修复前需要先核对线上部署版本和调用量。
8. 分仓拆单
分仓只支持配置中的特定场景,当前包括 e_app_im、已废弃标注的 e_shop_im 和 e_app_activity。
flowchart TD
A[源订单商品 inv_id + qty] --> B[BranchStoreOrderSplitSer]
B --> C[读取服务站门店/仓库能力]
B --> D[读取商品库存和扩展信息]
C --> E[计算每个仓可分配数量]
D --> E
E --> F{所有商品都能分配吗}
F -- 是 --> G[返回按仓拆分结果]
F -- 否 --> H[返回缺货或无法分配明细]
G --> I[上游按拆分结果创建子订单]
入口 BranchStore::split() 会校验 contact_id、sid、items 和场景,再调用 splitOrder()。查询“为什么订单分到了这个仓”时,应同时保留:
- 输入商品和数量;
- 候选门店/仓库;
- 各仓实时库存;
- 场景是否在允许拆单列表;
- 最终分配结果和未满足数量。
9. 移动商城商品查询数据流
MoveMallGoodsSer 并不是单纯查 t_bs_goods,它会叠加客户、仓储和场景规则。
flowchart LR
Q[查询条件/VIN/分类/品牌] --> G[物料缓存或 ES]
G --> S[服务站可售过滤]
S --> P[客户价/微仓价/最近采购价]
S --> N[服务站普通仓库存]
S --> M[客户微仓库存]
S --> K[快准供给库存]
P --> R[商品结果]
N --> R
M --> R
K --> R
R --> L[搜索日志和展示排序]
关键查询方法:
| 方法 | 业务用途 |
|---|---|
getGoodsList/goodsDetail/goodsContent | 旧移动商城商品列表和详情 |
getMoveGoodsPrice | 按客户、仓库和价格类型获取移动商城价格 |
getMoveStorage/getSelfStorage/getNormalMoveStorage | 区分移动仓、自有仓和普通仓库存 |
getPickGoodsList | 旧微仓领料 |
getMoveSendGoodsList | 马上送商品 |
eGoodsList/eSaasGoodsList | E站和 E-SaaS 商品 |
eCanSale/getSaleableInvIds | 判断商品是否可售 |
queryGoodsByContext | 给会话/智能询价按上下文查询 |
“页面有货但下单失败”必须先确认结果里显示的是哪类库存,不能直接拿服务站实时库存解释全部场景。
10. 微仓补货主流程
10.1 状态机
| 值 | 状态 | 进入条件 |
|---|---|---|
0 | 待出库 | 枚举保留;当前 addMoveSupply() 创建后直接进入 1 |
1 | 待派送 | 补货单和调拨单创建并完成调拨出库 |
2 | 派送中 | opt_tag=DELIEVER |
3 | 待上架 | E站 opt_tag=CONFIRM 确认收货 |
4 | 已上架 | 调拨入库全部完成 |
5 | 已取消 | 取消路径由其他入口控制,本文尚未确认完整状态约束 |
stateDiagram-v2
[*] --> 待派送: 创建补货单并调拨出库
待派送 --> 派送中: DELIEVER
派送中 --> 待派送: DEL_CANCEL
派送中 --> 待上架: CONFIRM
待上架 --> 已上架: 全部调拨入库
已上架 --> [*]
10.2 创建和出库时序
sequenceDiagram
autonumber
participant OPS as 服务站人员
participant MS as MoveSupplySer
participant M as 补货主明细表
participant TF as InvTfService
participant INV as 库存
OPS->>MS: addMoveSupply(entries, 微仓)
MS->>MS: 校验商品、出库仓/货位、微仓价
MS->>M: 写补货主表 bill_status=1
MS->>M: 写补货明细和源/目标仓
MS->>TF: addTf(is_move_supply=1)
TF->>TF: 创建调拨单
MS->>TF: changestatusTf
TF->>INV: 调拨出库,扣源仓库存
MS-->>OPS: 事务提交,待派送
创建补货单不是只写两张 t_scm_move_supply* 表:saveTf() 会同步创建调拨单并执行调拨出库,补货明细 ID 被写入调拨明细 srcOrderEntryId,用于上架时逐行反查。
10.3 确认和上架
- E站调用
MsOrder::msConfirm(),补充opt_tag=CONFIRM。 - 只有派送中状态可以进入待上架,并记录确认人、手机号、姓名和时间。
- 上架调用
moveSupplyIn()。 verifyInTf()校验补货单、关联调拨单、明细、商品和目标货位。- 已经写过调入货位的商品会报“已上架,请勿重复操作”。
- 调用
InvTfService::formTinTf()完成调拨入库。 - 明细
is_shelve=1并记录in_area_id。 - 调拨单全部入库后,补货主单进入已上架。
11. 移动退货
| 值 | 状态 | 含义 |
|---|---|---|
1 | 已退货 | 已从微仓退回/完成出库动作 |
2 | 部分上架 | 部分商品已回到服务站仓 |
3 | 已上架 | 全部完成入库上架 |
主表和明细表:
t_scm_move_re_order;t_scm_move_re_order_info。
MoveReOdSer::moveReSkuIn() 负责移动退货扫码上架。排查时要同时核对退货明细上架标志、目标货位、关联调拨/出入库流水和实时库存,不能只看退货主状态。
12. 旧智能询价链路
旧链路由 RobotInquirySer 接收微信群通知:
sequenceDiagram
autonumber
participant WX as 微信机器人
participant RI as RobotInquirySer
participant C as 客户/群绑定
participant AI as AI/VIN解析
participant G as MoveMallGoodsSer
participant R as 微信回复
WX->>RI: 群/好友消息
RI->>C: 按 chatId 找客户和开关
alt 未开查询物料
RI-->>WX: 静默跳过
else 已开通
RI->>RI: 判断文本/语音/VIN图片/VIN文本
opt 语音且 AI 报价开关开启
RI->>AI: 语音转文字
end
opt VIN
RI->>AI: VIN/图片解析车型
end
RI->>G: 按分类、车型、客户查询价格和库存
G-->>RI: 商品、价格、库存
RI->>R: 格式化有货/无货和价格
R-->>WX: 文本或链接回复
end
旧会话使用 Redis Hash,默认有效期 3600 秒,保存 VIN、车型压缩 ID、搜索词、仓库、用户、服务站、客户等上下文。旧会话过期后,关键词消息会被判定为“会话过期或无任何会话”。
13. RobotV2 架构
RobotV2 把输入、业务状态和输出拆开:
flowchart LR
RAW[微信/腾讯IM/钉钉/API原始消息] --> ADAPTER[Input Adapter]
ADAPTER --> CTX[MessageContext + traceId]
CTX --> INTENT[IntentFactory/PriorityResolver]
INTENT --> CM[ConversationManager]
CM --> STATE[State Machine]
STATE --> QUERY[QueryDispatcher]
QUERY --> POST[库存/选项/排序/用量后处理]
POST --> REPLY[Text/Card/Image/Composite Reply]
REPLY --> FORMAT[平台 Formatter]
FORMAT --> SENDER[Wechat/TencentIM/Dingtalk Sender]
13.1 消息适配层
输入适配器包括:
DefaultAdapter;WechatAdapter;TencentImAdapter;DingtalkAdapter。
输出层按平台选择 Formatter 和 Sender。业务状态机只产出统一 ReplyData,不直接拼每个平台的最终协议。
13.2 会话状态
| 状态 | 类型 | 含义 |
|---|---|---|
idle | 稳定 | 等待新意图 |
vin_parsing | 瞬态 | 解析 VIN |
vin_parsed | 稳定 | 已有 VIN 和车型上下文 |
car_model_confirming | 稳定 | 多车型时等待用户选择 |
product_querying | 瞬态 | 执行商品查询 |
product_list | 稳定 | 已展示商品,等待筛选或下单 |
order_placing | 瞬态 | 执行下单 |
image_recognition | 瞬态 | 识别图片 |
ai_parsing | 瞬态 | 等待/处理 AI 解析 |
expired | 稳定 | 会话已过期 |
stateDiagram-v2
[*] --> idle
idle --> vin_parsing: VIN文本/图片
vin_parsing --> car_model_confirming: 多车型
vin_parsing --> vin_parsed: 单车型
car_model_confirming --> vin_parsed: 用户选择
idle --> product_querying: 无VIN关键词
vin_parsed --> product_querying: 配件意图
product_querying --> product_list: 查询完成
product_list --> product_querying: 修改条件
product_list --> order_placing: 选择商品下单
order_placing --> product_list: 下单完成或返回
idle --> image_recognition: 图片
image_recognition --> ai_parsing: 异步AI
ai_parsing --> product_querying: 识别为配件
瞬态状态会立即处理并转换,不等待用户输入;稳定状态才会保留上下文等待下一条消息。
14. RobotV2 查询场景
| QueryScenario | 值 | 输入和用途 |
|---|---|---|
| 易损件 | CONVERSATION_KW_FL | VIN + 分类关键词 |
| 维保件 | CONVERSATION_KW_WBYL | VIN + 维保关键词 |
| 全车件 | CONVERSATION_KW_CARPARTS | VIN + 标准名称,支持 OE 分组 |
| 无 VIN 车型件 | CONVERSATION_KW_NO_CAR_VEHICLE_PARTS | 无 VIN 关键词 |
| 无 VIN 电池 | CONVERSATION_KW_BATTERY | 电池参数/关键词 |
QueryDispatcher 会选择:
WearingPartsQueryService;UpkeepPartsQueryService;CarPartsQueryService;NoVinKeywordQueryService;BatteryQueryService。
并可按开关串联后处理:
StorageDetailsProcessor:仓库和库存详情;OptionContentProcessor:选项展示;ProductSortProcessor:商品排序;MinUsageProcessor:最小用量。
14.1 新旧实现仍处于桥接状态
RobotV2/Services/ProductQueryService 中,易损件和电池通过反射调用旧 RobotInquirySer 的查询方法;机油和轮胎分支在该文件中仍标注“暂未实现”,过滤方法也仍是 TODO。RobotV2 的其他 Query Service 已有独立实现,因此排查时必须确认实际走的是 ProductQueryService 兼容层还是 QueryDispatcher 新链路。
15. AI 异步图片回调
robotV2AiCallback() 与旧 receiveMsgCallback() 分离,支持从以下位置兼容提取预会话 ID:
callbackParams.preConversationId
preConversationId
pre_conversation_id
AI 结果关心:
isPart;productName;productType:代码注释为1轮胎、2电池、3油品;data.productModel等识别结果;- 原始触发消息
msgId。
处理链路:
sequenceDiagram
participant U as 用户图片
participant RV2 as RobotV2
participant PRE as 预会话表
participant AI as AI中心
participant CB as robotV2AiCallback
participant F as RobotFacade
U->>RV2: 发送旧件/商品图片
RV2->>PRE: 保存预会话和原始 msgId
RV2->>AI: 异步识别 + preConversationId
AI-->>CB: 识别结果回调
CB->>PRE: 恢复上下文
CB->>F: 继续 Intent 和状态机
F-->>U: 回复商品、失败提示或静默忽略
如果旧件识别失败,是否回复由客户 old_part_reply 开关控制;关闭时会返回“ignored”而不发消息。排查“回调 200 但用户没收到回复”时必须检查这个分支。
16. 机器人下单
旧 RobotOrderSer::createPickOrder():
- 根据移动用户取得服务站和微仓
storage_id。 - 生成
RB{sid}{日期}{随机数}订单号。 - 检查指定仓、商品和正库存记录。
- 组装
entries、金额和折扣。 - 调用
SaOrderSer::saCreate2()。
它仍使用已废弃的 pick 场景,因此当前是否仍作为生产入口必须确认。RobotV2 新 OrderService 使用状态机中的 order_placing 处理下单,两条路径不要混为一谈。
机器人报价转订单还会受以下配置影响:
t_scm_robot_order_qty_config:品类默认下单数量和启用状态;t_scm_robot_quote_config/_info:报价范围;t_scm_robot_quote_replay_config:报价回复模板;- 客户或群的询报价开关、私聊开关和绑定关系。
17. 核心数据表
| 表 | 作用 |
|---|---|
t_scm_sass_order_rel | SAAS/E站源订单与 DGJ 销售单关系 |
t_scm_move_supply | 微仓补货主表 |
t_scm_move_supply_info | 补货明细、源/目标仓和上架标志 |
t_scm_move_re_order | 移动退货主表 |
t_scm_move_re_order_info | 移动退货明细 |
t_scm_move_supply_shield | 补货屏蔽配置 |
t_scm_move_replenish_record | 推荐补货记录 |
t_bs_move_storage_price | 微仓商品价格 |
t_admin_move_main | 主营产品/分类配置 |
t_scm_robot_conversation | 机器人正式会话 |
t_scm_robot_pre_conversation | 旧预会话 |
t_robot_conversation_pre | RobotV2 预会话 |
t_scm_robot_conversation_record | 会话消息记录 |
t_scm_robot_conversation_keyword | 会话关键词 |
t_scm_robot_parts_category | 全车件关键词/标准分类 |
t_scm_robot_no_car_vehicle_parts | 无车车型件关键词 |
t_scm_robot_position_keyword | 方位关键词 |
t_scm_robot_quote_config | 报价范围主配置 |
t_scm_robot_quote_config_info | 报价范围明细 |
销售订单、出库、库存和支付表详见 04_销售_出库_配送与收款.md,这里不重复维护另一套口径。
18. 消息和外部系统
| 动作 | 发送/接收 | 目的 |
|---|---|---|
sendMailToMq | DGJ 发出 | 按 E站场景通知接单/站内消息 |
sendInventoryEvent | DGJ 发出 | 即时出库或库存变化事件 |
sendWechatOrderToSaas | DGJ 发出 | 微信/E站订单同步 SAAS |
sendOutBoundToSaas | DGJ 发出 | 出库状态同步 SAAS |
sendInvoiceToSass | DGJ 发出 | 销售出库单同步 SAAS |
| 微信机器人消息 | DGJ 接收 | 群聊、好友和 IM 询价 |
| AI 图片识别回调 | DGJ 接收 | 恢复预会话并继续 RobotV2 |
| VIN/车型/商品查询 | DGJ 调用外部中心 | 识别车型和适配商品 |
消息名称相近但业务事实不同:订单创建消息、出库消息和库存事件不能互相替代。
19. 常用排查 SQL
19.1 查源订单与 DGJ 销售单关系
SELECT id, sass_order_id, sa_order_id, order_source, apply_id
FROM t_scm_sass_order_rel
WHERE sass_order_id = :source_order_id
OR sa_order_id = :sa_order_id;
销售主表是按 sid 分片的业务表,查询前按项目既有规则定位对应分表。
19.2 查补货单和明细
SELECT id, sid, bill_no, storage_id, bill_status,
total_qty, total_amount, out_time, deliever_time,
confirm_time, in_time
FROM t_scm_move_supply
WHERE sid = :sid
AND (id = :bill_id OR bill_no = :bill_no);
SELECT id, iid, sku_id, inv_id, qty,
out_storage_id, out_area_id, in_storage_id, in_area_id,
is_shelve
FROM t_scm_move_supply_info
WHERE sid = :sid
AND iid = :bill_id;
19.3 查机器人会话
SELECT id, state, contact_id, create_time, update_time
FROM t_scm_robot_conversation
WHERE id = :conversation_id;
SELECT *
FROM t_robot_conversation_pre
WHERE id = :pre_conversation_id;
字段以目标环境真实结构为准,尤其 RobotV2 目录中包含多份迁移 SQL,环境可能尚未全部执行。
20. 按现象排查
20.1 E站返回下单失败或重复创建
- 查
KEY_MOVE_OD_ADD_LOCK是否把完全相同请求判为短时间重复。 - 查
order_no是否已经在当前sid的销售订单分表存在。 - 查
station_id和contact_id是否正确。 - 查服务站物料缓存能否按全部
skuId返回商品。 - 查非赠品价格是否为 0、数量是否大于 0。
- 确认调用的是
createOrder,不是存在参数风险的旧create。
20.2 下单成功但 DGJ 没出库
这是普通 E站场景的正常边界。继续检查:
- 是否创建了
SaOrderTake接单记录; - 接单方是否处理;
- 销售订单状态是否推进;
- 是否生成销售出库单;
- 出库后才查库存流水和 SAAS 出库同步。
20.3 APP 金额和 DGJ 明细金额不一致
- 确认是否属于
E_APP_SCENES。 - APP 场景明细用
disAmount作为落库amount。 - 对照上游优惠券分摊,不要用原价直接重算。
- 再核对主单
totalAmount + disAmount和明细合计口径。
20.4 补货单卡在待派送/派送中/待上架
- 待派送:查是否执行
DELIEVER。 - 派送中:查 E站是否执行
CONFIRM,确认人字段是否完整。 - 待上架:查关联调拨单是否为已出库或部分入库,目标货位是否有效。
- 部分商品上架:查调拨明细
entryId/srcOrderEntryId和补货明细is_shelve。 - 已上架但库存不对:进入调拨入库流水和实时库存排查。
20.5 机器人不回复
flowchart TD
A[机器人不回复] --> B{消息是否进入回调/消费者}
B -- 否 --> C[查微信/IM入口和消息消费]
B -- 是 --> D{客户和群绑定存在吗}
D -- 否 --> E[修复 chatId/contact 绑定]
D -- 是 --> F{support_query_material/询报价开关开启吗}
F -- 否 --> G[代码可能静默跳过]
F -- 是 --> H{识别成何种 Intent}
H --> I{会话是否有效/状态是否合法}
I -- 否 --> J[查预会话、正式会话和过期时间]
I -- 是 --> K{查询结果为空还是发送失败}
K --> L[查 Query Service、库存价格和 Formatter/Sender]
20.6 AI 回调成功但用户仍没收到消息
- 检查
preConversationId三种字段名是否都没取到。 - 检查原始
msgId是否保留;代码注释明确不能使用回调新消息 ID。 - 检查预会话是否存在并已关联正式
conversation_id。 - 旧件识别失败时检查
old_part_reply,关闭会静默 ignored。 - 检查
autoSend、平台 Sender 和最终发送返回。
21. 代码检索命令
rg -n "createOrder|saCreate2|order_scene|payMode" application/controllers/inner/moveMall application/Services/SaOrders
rg -n "MOVE_SUPPLY_STATUS|updateMsStatus|moveSupplyIn|saveTf" application/Services/MoveMall
rg -n "RobotFacade|preConversationId|QueryDispatcher|StateNames" application/Services/MoveMall/RobotV2 application/controllers/inner/moveMall/Robot.php
rg -n "sendWechatOrderToSaas|sendOutBoundToSaas|sendInvoiceToSass|sendInventoryEvent" application/Services
rg -n "t_scm_sass_order_rel|SCM_MOVE_SUPPLY|SCM_ROBOT_CONVERSATION" application
22. 改动风险和回归清单
22.1 E站订单
- [ ] 七种有效
order_scene都能映射到正确sourceType和消息类型。 - [ ]
CHARGE/WEXPAY/UnionPay映射到正确账户、结算方式和支付状态。 - [ ] 同一
order_no重复请求不能生成第二张销售单。 - [ ] APP 优惠券分摊后的明细金额正确。
- [ ] 源订单、源明细、DGJ 主单和 DGJ 明细关系完整。
- [ ] 事务失败时主单、明细和关系表一起回滚。
- [ ] 普通 E站场景只创建销售订单,不误做即时出库。
- [ ] 接单记录、场景消息和配送展示信息正常。
- [ ] 旧
create()的真实调用量和处理结果得到确认后再修改。
22.2 分仓和商品
- [ ] 允许和不允许分仓的场景分别验证。
- [ ] 单仓满足、多仓组合、部分缺货和全部缺货都能解释分配结果。
- [ ] 客户价、微仓价、服务站仓库存和供给库存不串口径。
- [ ] 不良品或不可售商品不会进入普通 E站商品结果。
22.3 微仓
- [ ] 补货单创建同时生成调拨并完成源仓出库。
- [ ]
DELIEVER/DEL_CANCEL/CONFIRM只能在合法状态执行。 - [ ] 部分上架和全部上架状态正确。
- [ ] 重复扫描同一商品货位会被拒绝。
- [ ] 调拨入库、补货明细和实时库存保持一致。
- [ ] 移动退货的部分上架、全部上架和库存流水一致。
22.4 RobotV2
- [ ] 微信、腾讯 IM、钉钉/API 输入能选到正确 Adapter。
- [ ] VIN 文本、VIN 图片、多车型、无 VIN 关键词和语音路径可用。
- [ ] 所有瞬态状态最终进入稳定状态或明确失败。
- [ ] 易损件、维保件、全车件、无车件和电池查询分别回归。
- [ ] 库存详情、选项、排序和最小用量后处理不丢商品。
- [ ] AI 异步回调能恢复预会话和原始消息。
- [ ] Fallback、旧件识别失败、空结果和发送失败均有可观测日志。
- [ ] 机器人选品转订单后能回查 DGJ 销售单和源关系。
23. 源码证据索引
| 主题 | 证据文件 |
|---|---|
| 场景与补货状态 | application/KzData/Enums/MoveMallEnums.php |
| 移动订单入口 | application/controllers/inner/moveMall/Order.php |
| 销售订单落库 | application/Services/SaOrders/SaOrderSer.php |
| 销售来源 | application/KzData/Enums/SaOrderEnums.php |
| 分仓 | BranchStore.php、BranchStoreOrderSplitSer.php |
| 移动商品 | Material.php、MoveMallGoodsSer.php |
| 微仓补货 | MoveSupply.php、MsOrder.php、MoveSupplySer.php |
| 移动退货 | MoveReOd.php、MoveReOdSer.php |
| 旧机器人 | RobotInquirySer.php、MoveRobotEnums.php |
| RobotV2 总入口 | RobotV2/RobotFacade.php |
| RobotV2 会话和状态 | ConversationManager.php、States/StateNames.php |
| RobotV2 查询 | QueryDispatcher.php、Models/QueryScenario.php |
| AI 回调 | controllers/inner/moveMall/Robot.php |
| MQ | application/Services/Mq/MqSer.php |
| 表常量 | application/config/tables.php |
24. 待确认项
- 线上是否仍有流量调用
Order::create(),以及线上部署代码是否也存在saCreate2()漏传参数。 - 普通 E站销售单从“待接单”到“出库、配送、收款”的实际跨服务负责人和消息时序。
e_shop_im虽标注废弃但仍在分仓白名单,是否可彻底移除。- RobotV2 各平台当前实际启用范围,以及旧
RobotInquirySer的迁移进度。 - RobotV2 目录中的多份数据库迁移在各环境执行到哪一版。
- AI 异步回调的超时、重试、重复回调幂等和告警配置。
ProductQueryService中机油、轮胎 TODO 是否仍被生产调用。- 移动退货完整出库来源和关联调拨链,需要结合真实测试单继续补证。
这些问题必须在接口改造或旧链路下线前确认,但不妨碍按本文理解当前代码主路径。
请求-日志-数据变更追踪卡
多入口请求链路
| 场景 | 调用方与入口 | 请求载荷/上下文 | Controller/Consumer | Service/Provider | 汇合点 | 最终业务事实 |
|---|---|---|---|---|---|---|
| E站商品/下单 | 移动商城经 /inner/moveMall/* | sid、用户、SKU、数量、价格、收货信息 | controllers/inner/moveMall/Order.php 等 | SaOrderSer、移动商城 Service | E站来源单号 + 销售单号 | 生成销售单,后续复用销售履约链 |
| 微仓补货/调拨 | E站补货页面或内部任务 | 供需站点、SKU、数量、仓库 | MoveSupply.php、MsOrder.php | MoveSupplySer.php | 补货/调拨单号 | 形成供给申请、调拨和收货事实 |
| 旧机器人询价 | IM/机器人旧接口 | 会话、车型/VIN、商品问题 | 旧 Robot Controller | RobotInquirySer.php | 会话 ID | 记录询价上下文并返回匹配结果 |
| RobotV2 会话 | IM/Udesk/移动端 | 平台、用户、会话、消息、场景 | RobotV2 Controller/Facade | RobotFacade、ConversationManager、QueryDispatcher | conversation ID | 会话状态推进,查询结果和消息记录落库 |
| AI 异步回调 | AI 服务回调 /inner/moveMall/Robot | conversation/request ID、模型结果 | controllers/inner/moveMall/Robot.php | RobotV2 回调处理 | 请求 ID + conversation ID | 将异步结果合并进会话并推送响应 |
日志证据矩阵
| 链路段 | 日志来源 | 可检索锚点 | 成功信号 | 失败信号 | 与下一段关联方式 |
|---|---|---|---|---|---|
| E站请求 | Inner API/Controller 日志 | URI、sid、用户、来源单号 | 返回商品/订单业务 ID | 上下文缺失、商品不可售、参数异常 | 来源单号映射销售单号 |
| 销售汇合 | SaOrderSer 与销售链日志 | E站来源类型、销售单号 | SCM_SA_ORDER* 主明细提交 | 重复来源单、价格/库存校验失败 | 销售单号进入出库配送链 |
| 微仓履约 | MoveSupplySer/调拨 Service | 补货单、供需 sid、SKU | 调拨/收货数量推进 | 供给不足、重复收货、状态冲突 | 补货单号串联调拨和库存流水 |
| RobotV2 | RobotFacade、会话管理和查询分发异常 | platform、conversation ID、request ID、场景 | 会话记录新增且状态按场景推进 | 状态无处理器、查询异常、AI 超时 | conversation/request ID 关联回调和消息 |
| AI 回调 | moveMall/Robot.php | request ID、conversation ID、回调状态 | 回调幂等落库并完成响应 | 找不到会话、重复回调、结果解析失败 | 同一 ID 回查会话和记录表 |
环节数据变更台账
| 步骤 | 代码位置 | 事务 | 读取事实 | 写入表/缓存/MQ | 字段或数量变化 | 回查证据 |
|---|---|---|---|---|---|---|
| E站创建订单 | moveMall/Order.php -> SaOrderSer | 销售事务 | 服务站、商品、价格和收货信息 | SCM_SA_ORDER*、来源关系 | insert;来源标记为 E站;状态进入待履约 | 来源单号、销售单号、明细金额/数量 |
| 补货申请 | MoveSupplySer | 补货事务 | 微仓库存、供给站库存、申请量 | SCM_MOVE_MAIN 及补货/明细相关表 | 申请状态初始化;申请量写入 | 补货单号、供需站点、SKU |
| 调拨收发 | 调拨/库存 Service | 每次发货或收货事务 | 可发、在途、已收数量 | 调拨明细、SCM_INVENTORY* | 发出站库存 old-n,在途 old+n;收货站库存 old+n,在途 old-n | 调拨单 + 两站库存流水数量守恒 |
| 创建会话 | ConversationManager | 会话事务 | 平台用户、已有活跃会话 | SCM_ROBOT_CONVERSATION、预会话/记录表 | 新建或复用会话;state: old -> next;消息记录 insert | conversation ID、平台用户、消息时间 |
| 查询/AI 请求 | QueryDispatcher、AI Provider | DB 与外部 AI 非同一事务 | 会话状态、场景参数、历史消息 | 会话记录、外部请求/MQ | 记录用户消息和 request ID;状态进入等待结果 | request ID、场景、Provider 返回码 |
| AI 回调完成 | Robot 回调处理 | 回调事务 | request ID 对应等待记录 | 会话/记录表、推送渠道 | 等待态 -> 完成/失败;响应消息 insert;重复回调不重复写 | request ID 唯一性、会话状态和消息条数 |
子模块级独立追踪
子模块追踪:eshop-order E站销售订单创建
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 创建 | E站 inner/moveMall/Order V2 | request ID、sid、用户、来源单、SKU、价格、地址 | application/controllers/inner/moveMall/Order.php -> SaOrderSer | 用户站点、商品可售、价格、来源唯一性 | 销售事务 insert SCM_SA_ORDER/INFO/EXT;状态无 -> 待履约,来源字段写 E站 | request ID + sourceOrderNo + sale billNo | 响应未知按来源单查重;校验失败零写入,不重试旧 create() 风险入口 |
| 后置 | 销售 commit 后 | sale billNo、source type、message ID | application/Services/Mq/MqSer.php / 销售同步 Service | 已提交销售快照 | DB 不变;MQ/下游状态无 -> 待消费,异步边界 | sale billNo + routing key + message ID | 发送失败只补消息,不重复创建销售单 |
子模块追踪:eshop-split E站分仓拆单
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 计算拆分 | E站下单校验后 | request ID、source order、SKU、供给仓/站、qty | application/Services/SaOrders/SaOrderSer.php 及供给/分仓 Service | 各商品供给关系、仓库库存、配送地址 | 计算阶段只读,不写业务表;商品集合 -> 按供给维度分组 | request ID + source order + SKU/warehouseCode | 无供给或拆分不完整拒绝整单/按合同返回,不能静默丢商品 |
| 落单 | 每个分仓组 | source order + split key、内部 sale billNo | application/Services/SaOrders/SaOrderSer.php | 已冻结拆分结果、来源幂等关系 | 事务 insert 多张销售主明细;来源关系 0 -> n,分单数量合计=原请求 | source order + split key + all billNos | 部分成功按 split key 补缺失分单;禁止整单重放产生重复分单 |
子模块追踪:eshop-goods 移动商城商品查询
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 查询 | E站商品列表/搜索/详情 | request ID、sid、关键词、SKU、分页、场景 | application/controllers/inner/moveMall/* -> Materiel/MoveMall 查询 Service | 商品主数据、站点商品、价格、实时/供给库存、范围 | 只读,不写业务表;返回 DB/cache/ES 当前快照 | request ID + sid + SKU/query;记录命中的查询源 | DB 有页面无时分别查范围、价格、库存和缓存版本,不手工改商品状态 |
| 缓存回查 | 商品中心通知或缓存任务后 | SKU、update time、cache key/index | application/Services/Materiels/* / Cache Service | MySQL 最新事实和同步水位 | Redis/Mongo/ES doc old -> new,属于异步边界 | SKU + update time + task/message ID | 派生层失败只补索引/缓存,以 DB 为真相源 |
子模块追踪:move-supply 微仓补货
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 申请 | E站补货入口 | request ID、需求/供给 sid、SKU、申请量 | application/controllers/moveMall/MoveSupply.php -> MoveSupplySer | 微仓库存、供给能力、已有申请 | 补货事务 insert SCM_MOVE_MAIN 及明细;状态无 -> 待处理,申请量= n | request ID + supply billNo + two sids + SKU | 重复来源按申请业务键查原单;供给不足零写入 |
| 履约 | 供给确认、发货/收货动作 | supply billNo、SKU、out/in qty | application/Services/MoveMall/MoveSupplySer.php -> 调拨/库存 Service | 可发、在途、已收和当前状态 | 每步事务 out/in qty old -> old+n,状态按待发/在途/完成推进 | supply/transfer billNo + transType | 部分成功只补对应发/收环节,数量合计必须守恒 |
子模块追踪:move-transfer 微仓调拨与收货
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 发出 | 微仓/调拨出库 | request ID、transfer billNo、from/to sid、SKU、qty | application/controllers/moveMall/MsOrder.php -> 调拨 Service -> InventorySer | 发站实时库存、可发量、仓位 | 出库事务发站 qty old -> old-n,写库存流水;在途 old -> old+n | transfer billNo + from sid + SKU + transType | 库存不足 rollback;发出已成状态未写只补状态/关系,不二次扣库 |
| 收货 | 目标站签收 | request ID、transfer billNo、to warehouse/location、received qty | application/Services/MoveMall/MoveSupplySer.php -> InventorySer | 可收量、在途量、目标仓位 | 入库事务目标 qty old -> old+n、在途 old -> old-n;全收后状态 -> 完成 | transfer billNo + two station inventory flows | 重复收货 0 增量;超收拒绝,差异按原调拨单补偿 |
子模块追踪:move-return 移动退货
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 创建 | 移动商城退货入口 | request ID、原移动/销售单、SKU、qty、原因 | application/controllers/moveMall/MoveReOd.php -> MoveReOdSer | 原履约量、已退/申请量、来源关系 | 退货事务 insert 退货主明细;申请量 0 -> n,状态无 -> 待处理 | request ID + original/return billNo + SKU | 超退/重复来源零写入;未知响应先按来源关系查重 |
| 出入库/完成 | 审核、仓内动作或回调 | return billNo、SKU、warehouse/location、message ID | application/Services/MoveMall/MoveReOdSer.php -> Inventory/MQ Service | 审核态、可操作量、库存和退款事实 | 事务按方向 qty old +/- n,退货状态 old -> completed;MQ 为异步边界 | return billNo + transType + message ID | 库存、退款、状态分段补偿,禁止重跑整条退货链 |
子模块追踪:robot-v1 旧智能询价
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 输入识别 | 旧机器人/微信询价 | request ID、用户、conversation ID、VIN/车型/关键词 | application/Services/MoveMall/RobotInquirySer.php | 会话上下文、车型/VIN、商品和报价规则 | 会话/搜索记录 insert 或 context old -> new;业务订单不写 | conversation ID + request ID + scenario | 解析失败保留原输入并返回可人工处理状态,不伪造商品结果 |
| 返回报价 | 旧询价 Service 后半段 | conversation ID、candidate SKU、price source | application/Services/MoveMall/RobotInquirySer.php -> 商品/报价 Service | 候选商品、价格库存、站点范围 | 只读商品价格;回复记录由无 -> 有 | conversation + SKU + price rule/source | Provider/查询失败记录失败段,可重试查询,不重复创建会话 |
子模块追踪:robot-v2 RobotV2 会话与查询
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 会话处理 | IM/移动端消息 | message ID、platform、user、conversation ID、text | application/Services/MoveMall/RobotV2/RobotFacade.php -> ConversationManager/Handler | 活跃会话、state/context、消息幂等键 | 会话事务 insert conversation/record;state/context old -> next | message ID + platform user + conversation ID | 重复消息 0 新回复;无 handler 记录 state 和输入后转人工/失败 |
| 场景查询 | Handler 分发查询 | conversation/request ID、scenario、normalized params | application/Services/MoveMall/RobotV2/QueryDispatcher.php | 场景模型、商品/订单/报价事实 | 查询通常只读;search log/record insert,state -> waiting/completed | conversation + request ID + scenario | 查询异常不跨场景重试;保留上下文供人工/补偿任务续跑 |
子模块追踪:robot-callback-order AI 异步回调与机器人下单
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| AI 回调 | /inner/moveMall/Robot | external request ID、conversation ID、AI result/status | application/controllers/inner/moveMall/Robot.php -> RobotV2 callback Service | waiting request、会话版本、是否已回调 | 回调事务 state waiting -> completed/failed,reply record insert;重复回调 0 变化 | external request ID + conversation ID + callback time | 找不到会话/旧回调告警;按 request ID 补写结果,不新建平行会话 |
| 机器人下单 | 用户确认报价/商品 | request ID、conversation/offer ID、SKU、qty、address | application/Services/MoveMall/RobotV2/* -> SaOrderSer | 已确认商品价格、用户站点、来源唯一性 | 销售事务 insert 主明细,source 绑定 conversation/offer;状态无 -> 待履约 | conversation/offer + sale billNo + request ID | 下单响应未知按来源 ID 查销售单;消息发送失败只补回复,不重做订单 |