1. 文档目标
财务、报表、MQ、OA 和同步任务不是一个单独业务模块,而是采购、销售、库存、活动、渠道和移动商城共同依赖的横向能力。本文要回答四个容易被混淆的问题:
- 钱有没有支付成功:以支付中心回调及其状态为依据。
- DGJ 财务事实有没有落库:看付款主单、付款明细、账户流水和采购支付扩展。
- 业务状态和下游系统有没有推进:看采购/预订单状态、活动状态和 MQ 消费结果。
- 报表什么时候可见、口径是否一致:区分实时业务表和 Hologres DWD 数据。
本文适用于:
- 采购或预订单显示支付成功,但采购状态、付款单或账户余额不对;
- 支付中心重复回调、异常回调或退款回调排查;
- 报表与业务页面金额、数量或状态不一致;
- OA 已审批但 DGJ 活动、额度或费用状态没有变化;
- 销售单、出库单已经完成,但 SaaS/APP/其他下游没有收到消息;
- MQ 队列无积压,但业务仍没有推进;
- 修改
MqSer、支付回调、报表或同步 Builder 前做影响评估。
本文结论来自当前代码静态核对。支付中心、OA、Hologres、RabbitMQ 和下游系统的生产重试、唯一键、数据延迟及网关合同仍需对应环境证据确认。
2. 先建立四层事实模型
flowchart LR
EXT["外部事实\n支付中心 / OA / 订单中心"]
MQ["消息事实\ndestination + routing key"]
BIZ["DGJ 业务事实\n订单状态、活动状态、库存"]
FIN["DGJ 财务事实\n付款单、明细、账户流水"]
DWD["分析事实\nHologres DWD"]
DOWN["下游事实\nSaaS / APP / OA / OPS"]
EXT --> MQ
MQ --> BIZ
MQ --> FIN
BIZ --> DWD
FIN --> DWD
BIZ -->|"dgj_notify 等"| DOWN
这四层不能相互替代:
| 现象 | 能证明什么 | 不能证明什么 |
|---|---|---|
| 支付中心返回成功 | 外部支付结果成功 | DGJ 已完成财务落账和订单推进 |
| MQ 已 ACK | 消费者确认了消息 | 所有业务分支都成功,部分代码会记录异常后 ACK |
| 采购单已更新 | 业务状态已推进 | 付款主明细、账户流水一定完整 |
| 付款单存在 | 部分财务事实存在 | 采购状态、活动、下游同步一定成功 |
| 业务库有数据 | 事务事实已落库 | Hologres DWD 已同步并可查询 |
| 报表有数据 | 分析层已有快照 | 当前实时业务状态与报表完全一致 |
排查必须沿“外部回调 -> 消息 -> 业务表/财务表 -> 下游/报表”逐层取证。
3. 代码入口地图
3.1 财务与支付
| 业务 | 入口 | 核心实现 |
|---|---|---|
| PC 收付款 | application/controllers/scm/Payment.php | 传统收付款单操作和 PaymentSer |
| 财务控制器 | application/controllers/financial/ | 财务查询和专项能力 |
| 账户管理 | application/controllers/saleAccount/Account.php | 账户及流水展示 |
| 支付中心回调 | application/controllers/tasks/PayCenterNotify.php | 支付、调额、提现、授信、退款等事件 |
| 全车件结算回调 | application/controllers/tasks/QPayCenterNotify.php | qpaycenter_notify 回调 |
| 收付款领域 | application/Services/Payment/PaymentSer.php | 收付款业务 Service |
| 还款领域 | application/Services/Payment/RepaymentSer.php | 授信还款单 |
| 账户缓存 | application/Services/BaseData/AccountSer.php | 系统账户创建后的缓存刷新 |
3.2 报表
| 业务 | 控制器 | Service/Provider |
|---|---|---|
| 采购订单、采退、入库 | controllers/reports/PurchaseReport.php | PurchaseReportSer -> PurchaseProvider |
| 销售明细、商品销售、对账 | controllers/reports/SaleReport.php | SaleReportSer / SaleProvider |
| 库存进销存 | controllers/reports/InventoryReport.php | InventoryReportSer |
| 利润 | controllers/reports/ProfitReport.php | ProfitReportSer / 报表 Provider |
| 客户分析 | controllers/reports/CustomerReport.php | CustomerReportSer 并行请求多个指标 |
| 应收/应付 | 报表和旧控制器入口 | AccountReceiveSer, AccountPaySer |
| 旧报表 | application/controllers/Report.php | 直接模型或历史实现 |
3.3 MQ、OA 和同步
| 业务 | 文件 | 职责 |
|---|---|---|
| MQ 常量 | application/KzData/Enums/MqEventEnums.php | destination、routing key 和部分关系表 |
| 通用生产者 | application/Services/Mq/MqSer.php | 库存、采购、APP、活动、OA、渠道、对账等事件 |
| 消费者任务 | application/controllers/tasks/*Notify.php | 按目标队列注册回调并 ACK/NACK |
| OA 回调 | tasks/OaNotify.php | 按模板码分发额度、费用、活动和秒杀审批 |
| OA 结果回传 | tasks/OaResultNotify.php | 调用 OA Provider 同步结果 |
| 销售同步 | Services/SyncOrder/SyncOrderSer.php | 将销售和出库事件发布到 dgj_notify |
| 同步报文 | Services/SyncOrder/SyncOrderBuilder.php | 把 DGJ 单据转换为下游合同字段 |
4. 财务数据模型
4.1 核心表
| 常量/模型 | 物理表 | 用途 | 关键字段 |
|---|---|---|---|
SCM_PAYMENT / PaymentModel | t_scm_payment_{sid % 32} | 收款、付款、其他收入/支出主单 | id, billNo, billType, transType, buId, totalAmount, billStatus, sid |
SCM_PAYMENT_INFO / PaymentInfoModel | t_scm_payment_info_{sid % 32} | 收付款业务、账户和来源单明细 | iid, billNo, amount, accId, wayId, paymentType, stlId, stlNo |
SCM_ACCOUNT | t_scm_account | 账户流水/当月账户事实 | 来源单、账户、方向和金额 |
SCM_PO_ORDER_PAYINFO | t_scm_po_order_payinfo | 采购/预订单在线支付拆分信息 | iid, billNo, amount, type, discount, rebate, refund, service_amount |
SCM_REPAYMENT_ORDER | t_scm_repayment_order | 授信还款单 | 还款单号、状态、金额和支付关系 |
SCM_PAYMENT_LOG | t_scm_payment_log | 付款操作日志 | 业务单和操作记录 |
SCM_PO_ORDER | t_scm_po_order | 采购状态和支付时间 | id, billNo, billStatus, paymentTime, srcOrderType |
SCM_PAYMENT_SUB_NUM = 32,主表和明细表都按:
payment_table = t_scm_payment_{sid % 32}
payment_info_table = t_scm_payment_info_{sid % 32}
因此只查无后缀旧表或错误分片,可能误判为“没有付款单”。
4.2 主单、明细和账户流水关系
erDiagram
PO_ORDER ||--o{ PO_PAYINFO : "iid / billNo"
PAYMENT ||--o{ PAYMENT_INFO : "iid"
PAYMENT_INFO ||--o{ ACCOUNT_FLOW : "业务来源与账户"
PO_ORDER ||--o{ PAYMENT_INFO : "stlId / stlNo"
PO_ORDER {
int id
string billNo
int billStatus
datetime paymentTime
}
PAYMENT {
bigint id
string billNo
string billType
decimal totalAmount
int sid
}
PAYMENT_INFO {
bigint iid
decimal amount
int accId
int stlId
string stlNo
}
PO_PAYINFO {
int iid
string billNo
int amount
string type
int rebate
int refund
}
一笔在线支付可能产生:
- 一张付款主单和多条付款明细;
- 多个支付账户对应的账户流水;
- 一条采购支付扩展记录;
- 若使用授信,额外生成“其他收入”主单、明细和账户流水;
- 若是预订单,每个支付账户分别生成一张其他支出单。
5. 支付中心消费者
5.1 队列和事件
目标队列:paycenter_notify。
| routing key | 回调方法 | 主要业务 |
|---|---|---|
paycenter_pay_result | paycenterPayResult() | 采购/预订单支付成功落账和状态推进 |
paycenter_balance_adjust | paycenterBalanceAdjust() | 账户额度调增/调减及其他收入/支出 |
paycenter_refund_account_withdraw_result | paycenterRefundWithdrawResult() | 待退款账户提现结果 |
paycenter_baitiao_return_pay_result | paycenterBaitiaoReturnPayResult() | 授信还款支付 |
paycenter_baitiao_expire | paycenterBaitiaoExpire() | 授信到期提醒 |
paycenter_pay_exception | paycenterPayException() | 支付抵扣异常,关闭待支付采购单 |
paycenter_installment_expire | paycenterInstallmentExpire() | 分期到期处理 |
paycenter_refund_result | paycenterRefundResult() | 退款结果 |
paycenter_baitiao_offline_return | paycenterBaitiaoOfflineReturnPayResult() | 线下授信还款结果 |
paycenter_baitiao_service_overdue_change | paycenterBaitiaoServiceOverdueChange() | 服务站授信逾期变化 |
本文重点展开 paycenter_pay_result;其他事件在第 18 篇财务和支付排查手册继续按请求字段逐项展开。
6. 支付成功回调合同
6.1 代表性请求
回调由 MQ 直接传入数组,不是普通 HTTP 控制器请求。代表性结构:
{
"sourceOrderNo": "PO...",
"sourceOrderType": "01",
"payOrderNo": "支付中心单号",
"mainPayTypeCode": "02",
"payStatus": "02",
"payStatusMsg": "支付成功",
"existsCashDiscount": "1",
"cashDiscountRate": "99.7",
"cashDiscountAmount": 70,
"detailTotalAmount": 345000,
"payTime": "2026-07-15 10:00:00",
"details": [
{
"payAccountTypeCode": "02",
"payAccountTypeDesc": "个人付款账户",
"payAmount": 340000,
"serviceAmount": 0
},
{
"payAccountTypeCode": "06",
"payAccountTypeDesc": "待退款账户",
"payAmount": 5000,
"serviceAmount": 0
}
]
}
重要字段:
| 字段 | 当前代码用法 |
|---|---|
sourceOrderNo | 查采购单或预订单的业务主键 |
sourceOrderType | 04 表示预订单;其他分支按采购单处理 |
payStatus | 仅字符串 02 视为成功 |
mainPayTypeCode | 主支付账户类型,影响混合支付和授信逻辑 |
detailTotalAmount | 支付中心金额,代码按分处理并除以 100 落付款主单 |
details | 按 payAccountTypeCode 重建映射,同类型重复行可能被后行覆盖 |
serviceAmount | 支付服务费,回调单位与 payAmount 相同 |
| 折扣/返利/退款字段 | 写采购支付扩展,用于采购详情和可优惠金额 |
6.2 支付账户类型
| 代码 | 含义 | 特殊处理 |
|---|---|---|
01 | 企业付款账户 | 主支付或混合支付组成 |
02 | 个人付款账户 | 主支付或混合支付组成 |
05 | 返利账户 | 写 rebate |
06 | 待退款账户 | 写 refund;若作为主支付类型会转换为 00 |
07 | 快准授信账户 | 生成授信借款“其他收入”单 |
08 | 他行融资账户 | 混合支付组成 |
10 | 工行 E 贷 | 描述中拆订单金额和服务费 |
12 | 银联付款账户 | 系统在线账户 |
15 | 专属返利账户 | 写 exclusive_rebate |
18 | 网商付款账户 | 混合支付组成 |
20 | 预充值账户 | 系统在线账户 |
99 | 0 元订单 | 跳过财务单据创建,直接推进订单状态 |
100 | 混合支付 | DGJ 计算出的展示类型,不是普通明细账户 |
系统账户号使用前缀 10 + payAccountTypeCode 避免与已有账户编号冲突。账户不存在时会创建,再调用 AccountSer::updateAccountCache() 刷新缓存。
7. 支付成功完整数据流
7.1 总流程
sequenceDiagram
participant PC as 支付中心
participant MQ as paycenter_notify
participant C as PayCenterNotify
participant O as 采购单/预订单
participant P as 付款主表/明细
participant A as 账户流水
participant PI as po_order_payinfo
participant ACT as 活动/秒杀
PC->>MQ: paycenter_pay_result
MQ->>C: 回调报文
C->>C: payStatus 必须为 02
C->>O: 按 sourceOrderType 查询并校验状态
C->>C: 开启数据库事务
C->>P: 创建付款或其他支出主单/明细
C->>A: saveAccounts()
C->>PI: 写支付方式、折扣、返利、退款、服务费
opt 授信支付
C->>P: 创建其他收入主单和明细
C->>A: 写授信账户流水
end
C->>O: 更新支付时间和业务状态
opt 秒杀采购单
C->>ACT: markPaidByPoOrder()
end
C->>C: 提交事务
C-->>MQ: ACK;异常时 NACK
7.2 来源订单分支
flowchart TD
A["sourceOrderType"] --> B{"是否 04 预订单"}
B -->|是| PRE["查询 preOrder"]
PRE --> PREST["允许待支付、取消、自动取消"]
PREST --> PREPAY["每个账户生成一张 QTZC 其他支出单"]
PREPAY --> PRESTATE["payedPreOrder() 推进预订单"]
B -->|否| PO["按 sourceOrderNo 查询采购单"]
PO --> POST["仅允许 wait_pay 或 pay_overtime"]
POST --> ZERO{"mainPayTypeCode 是否 99"}
ZERO -->|是| STATE["跳过财务单,采购状态改 submitted"]
ZERO -->|否| PAY["一张 FKD 付款主单 + 多条明细"]
PAY --> STATE
STATE --> FLASH{"srcOrderType 是否秒杀"}
FLASH -->|是| MARK["markPaidByPoOrder"]
7.3 采购单分支落账
采购单支付生成:
- 付款主单
FKD,billType=PAYMENT。 - 每个支付账户一条
PaymentInfo。 AccountService::saveAccounts()统一写账户事实。- 一条
PoOrderPayInfo记录主支付、混合支付、折扣、返利、退款和服务费。 - 授信支付额外生成其他收入主单和明细。
- 采购单状态更新为
BILLSTATUS_SUBMITED,写paymentTime和支付意见。 - 秒杀来源调用
FlashSaleSer::markPaidByPoOrder()。
7.4 预订单分支落账
预订单使用其他支出单:
billType=QTZC;- 每个支付账户单独生成一张付款主单;
- 明细金额先以元写
PaymentInfo,写账户流水前转换为负数; - 最后调用
PreOrderService::payedPreOrder()推进预订单。
8. 金额单位和计算
这是财务排查最容易出错的地方。
8.1 支付回调到财务表
付款主单 totalAmount = detailTotalAmount / 100
付款明细 amount = (payAmount + serviceAmount) / 100
含服务费主单总额 = (detailTotalAmount + 所有 serviceAmount) / 100
Payment 和 PaymentInfo 以“元”落库。
8.2 采购支付扩展
PoOrderPayInfo 直接保存支付中心原始数值:
amount = 主支付及部分混合支付 payAmount
discount = cashDiscountAmount
rebate = 返利账户 payAmount
exclusive_rebate = 专属返利 payAmount
refund = 待退款账户 payAmount
service_amount = serviceAmount 汇总
can_disc_amount = 可参与优惠的原始金额
这些值没有在回调中除以 100,应按支付中心原始单位理解。跨表对账时不能直接把 po_order_payinfo.amount 与 payment_info.amount 相加比较,应先统一单位。
8.3 混合支付判定
代码先把主账户金额放入 paymentPayInfo,再遍历明细:
- 若发现企业、个人、授信、融资或网商账户不是主支付方式,则类型改为
100混合支付并累加金额; - 如果组合中包含授信和退款/返利/专属返利,为采购详情展示会强制把类型改为授信;
- 主方式为
00、专属返利或待退款账户时,初始实付金额按0处理; - 主方式最终为待退款账户
06时,写入类型00。
因此“展示支付类型”和“实际支付账户明细”不是同一口径,真实拆分应查 PaymentInfo 和账户 ID。
9. 支付状态和幂等边界
9.1 收付款单状态
| 值 | 含义 |
|---|---|
0 | 已确认 |
1 | 已提交 |
2 | 已确认,V2 兼容值 |
6 | 已撤销 |
报表和应收应付查询通常排除 billStatus=6 和软删除数据。
9.2 支付成功回调的当前幂等方式
当前主要依赖来源订单状态:
- 采购单只接受待支付或支付超时;
- 预订单只接受待支付、取消或自动取消;
- 处理后状态改变,重复消息会因状态不允许而抛错。
需要注意:状态不允许时回调返回 NACK,不是把“已处理的重复成功消息”识别为 ACK。若 MQ 对 NACK 持续重投,可能形成重复告警或重试循环,必须确认生产重试和死信策略。
payOrderNo 在当前核心成功分支中没有被写成显式幂等键。应核对表唯一索引和支付中心回调合同,不能仅靠代码推断为强幂等。
9.3 事务边界
支付主流程在采购模型数据库连接上开启事务,付款、明细、账户、采购支付信息、订单状态和秒杀标记都在提交前执行。
事务提交后还会查询任务记录并调用 TaskSer::execute()。该调用仍位于 try 内:若它在事务已经提交后抛异常,catch 会尝试回滚并返回 NACK,但已提交的财务和订单数据无法真正回滚。重复投递时可能再次进入回调。这是必须重点回归的“提交后副作用”风险。
10. 支付异常回调
paycenter_pay_exception 代表账户抵扣等支付异常:
{
"sourceOrderNo": "PO...",
"exceptionDesc": "支付中心异常说明"
}
处理:
- 按采购单号查询 ID。
- 调用
PoOrderSer::cancelWaitPayOrder(orderId, true, message)。 - 成功返回
ACK。 - 订单不存在或关闭失败返回
NACK。
秒杀待支付采购单关闭时,释放锁定量和购物车/秒杀订单状态的具体逻辑由采购取消及 FlashSaleSer 共同完成,应同时对照第 05、17 篇。
11. 额度调整回调
paycenter_balance_adjust 先用 adjustNo 在付款明细中查重:
type=01:调增,生成其他收入;- 其他值:调减,生成其他支出,写账户时金额转负;
- 授信账户调整不在这里生成收入单,授信收入改在实际支付时生成;
- 账户不存在时自动创建并刷新缓存;
- 完成后发送账户主题站内信。
flowchart TD
MSG["balance_adjust"] --> IDEM{"adjustNo 是否已处理"}
IDEM -->|是| NACK["抛重复异常并 NACK"]
IDEM -->|否| TYPE{"type 是否 01"}
TYPE -->|是| IN["其他收入"]
TYPE -->|否| OUT["其他支出,账户金额取负"]
IN --> DETAIL["逐账户创建付款主明细"]
OUT --> DETAIL
DETAIL --> SKIP{"是否授信账户"}
SKIP -->|是| NO["跳过本次收入/支出生成"]
SKIP -->|否| ACC["saveAccounts"]
ACC --> NOTICE["发送站内信"]
重复调额当前也抛错并返回 NACK,需确认是否会被无限重投。
12. 报表系统架构
12.1 两类报表来源
flowchart LR
UI["报表页面"] --> CTRL["reports/* Controller\n权限 + 参数校验"]
CTRL --> TYPE{"数据来源"}
TYPE -->|实时/历史本地| MODEL["MySQL 分片表/业务表"]
TYPE -->|分析报表| SER["Report Service 生成 SQL"]
SER --> PROVIDER["Report Provider"]
PROVIDER --> HOLO["CENTER_API\n/devcenter/report/hologres/query"]
HOLO --> DWD["Hologres DWD"]
MODEL --> FORMAT["格式化/汇总/导出"]
DWD --> FORMAT
FORMAT --> UI
第一类直接查 DGJ 业务表或分片财务表,第二类由 DGJ 生成 SQL,再把 SQL、offset、size 发给中心报表接口查询 Hologres。
12.2 代表性采购报表请求
应用内入口:reports/PurchaseReport/purchaseOrderDetailReport。
{
"beginDate": "2026-07-01",
"endDate": "2026-07-15",
"page": 1,
"rows": 100,
"JXCSID": 10001,
"billNo": "可选采购单号",
"skuId": "可选SKU"
}
基础校验:
- 开始、结束日期必须为
Y-m-d; page、rows必须为数字;rows <= 1000;JXCSID > 0;- 先检查报表查询权限;导出使用独立导出权限。
返回统一结构:
{
"records": 120,
"page": 1,
"total": 2,
"limit": 100,
"rows": [],
"sum": []
}
12.3 Hologres 请求
{
"sql": "select ... from dwd_... where ...",
"offset": 0,
"size": 100
}
标准流程:
- 生成 count SQL 和列表 SQL。
- 请求 count,得到
total。 - 根据
page/rows计算 offset 和总页数。 - 请求当前页。
- 汇总可能循环请求全部分页,再在 DGJ 内计算合计。
- 把 DWD 字段转换成页面字段。
导出通常循环拉取所有分页,数据量大时会产生多次远程请求,应关注超时、内存和导出时间。
12.4 主要 DWD 表
| 报表 | DWD 表 | 主要口径 |
|---|---|---|
| 采购订单明细 | dwd_bbyh_dgj_scm_po_order | 采购数量、发货、待入、已入、关闭、金额 |
| 采购退货 | dwd_bbyh_dgj_scm_po_order_return | 采购退货单和明细 |
| 采购入库 | dwd_bbyh_dgj_scm_pu_invoice | 入库数量、金额、供应商和商品 |
| 应收应付余额 | dwd_dgj_home_arrears | 客户/供应商余额 |
| DGJ 出库 | dwd_dgj_out_bound | 出库单和商品事实 |
| 赠品活动 | dwd_dgj_activity_gift 等 | 活动订单采购退货和站点 |
| 销售对账 | dwd_bbyh_dgj_sa_invoice_payment_info | 销售出库与收款对账 |
| 销售历史 | dwd_dgj_sale_history | 历史销售搜索和分析 |
| 库存进销存 | dwd_bbyh_dgj_scm_inventory_all | 入库、出库、期末库存等 |
12.5 采购报表部分计算
采购订单明细把 DWD 字段转换为:
采购金额 = amount
已发数量 = outqty
待入数量 = waitqty
已入数量 = inqty
关闭数量 = closeqty
入库金额 = price * inqty - deduction * inqty / qty
当原采购 qty=0 时,入库金额公式存在除零风险;正常业务应不产生零数量采购明细,但历史/修复数据仍需防御。
13. 报表延迟和对账方法
13.1 正确排查顺序
flowchart TD
A["页面/业务表已更新,报表没有"] --> B["确认业务主表和明细真实落库"]
B --> C["确认状态、软删除、日期、sid 和分片"]
C --> D["确认对应 DWD 表是否已有业务键"]
D --> E{"DWD 是否存在"}
E -->|否| F["查同步任务/SLA/数据平台"]
E -->|是| G["对照报表 SQL where 和字段转换"]
G --> H["检查权限、日期边界、分页和汇总"]
不要为了解决报表延迟直接修改业务表;先判断是业务事实错误、ETL 未同步、报表 SQL 过滤还是展示计算错误。
13.2 时间口径
报表常见时间字段不统一:
- 业务单据日期
billDate; - 创建时间
create_time; - DWD 业务日期
dw_billdate; - 支付时间
paymentTime/payTime; - 入库/出库实际发生时间。
同一业务单按不同时间过滤可能落在不同日期范围。排查必须确认页面使用的具体字段,而不是只看用户选择的“日期”。
14. MQ 拓扑
14.1 主要 destination
| destination | 方向 | 典型业务 |
|---|---|---|
dgj | DGJ 内部异步 | 库存事件、采购关闭、延时取消、搜索日志、OPS 订单 |
ordercenter_notify | 订单中心 -> DGJ | 采购审核、售后、取消、关闭、自制订单 |
dispatchcenter_notify | 配送/调拨中心 -> DGJ | 发货出库、物流、自提、退货关闭 |
paycenter_notify | 支付中心 -> DGJ | 支付、退款、授信、提现、调额 |
itemcenter_notify | 商品中心 -> DGJ | 限购、套包、价格、生效失效、敏感词 |
oa_notify | OA -> DGJ | 审批结果 |
oa_result_notify | DGJ -> OA/费用系统 | OA 结果同步 |
dgj_notify | DGJ -> 下游 | 销售、出库、APP、活动和其他业务通知 |
dgj_channel | DGJ 渠道内部/OPS | 渠道人工单、审核、发货、退货 |
dgj_statement* | DGJ 对账任务 | 策略、生成、收款状态和发送 |
14.2 常见生产者
| 方法 | destination / routing key | 代表 payload |
|---|---|---|
sendInventoryEvent() | dgj / inventory_event | sid, 库存变动、流水 ID、单据和采购 ID |
sendInStorageEvent() | dgj_notify / dgj_po_in_storage | details |
sendPoCloseOrderConfirm() | dgj / po_close_order_confirm | 关闭确认参数 |
sendPauseCancel() | dgj / dispatch_pause_cancel_delay_60 | token |
sendSaOrderOutToMq() | dgj_notify / app_sa_order_out | 销售出库通知参数 |
sendSaOrderStatusToMq() | dgj_notify / app_sa_order_status | 配送状态参数 |
sendActivityStartToSaas() | dgj_notify / activity_start | sids |
sendActivityEndToSaas() | dgj_notify / activity_end | sids |
sendOAResult() | oa_result_notify / oa_result_notify_sync | id, status |
sendStatementStrategySync() | dgj_statement_strategy / statement_strategy_sync | 对账策略参数 |
sendStatementBillCreate() | dgj_statement / statement_bill_create | 对账单生成参数 |
sendStatementSend() | dgj_statement_send / statement_send | 发送任务参数 |
14.3 主要消费者
| 任务 | destination | 主要回调 |
|---|---|---|
OrderCenterNotify | ordercenter_notify | 采购审核、售后、取消、关闭、预订单、自制订单 |
DispatchCenterNotify | dispatchcenter_notify | 出库、物流、自提、退货关闭、标签变化 |
PayCenterNotify | paycenter_notify | 支付、退款、授信、调额、提现 |
ItemCenterNotify | itemcenter_notify | SKU 限购、套包、价格、敏感词 |
OaNotify | oa_notify | oa_audit |
OaResultNotify | oa_result_notify | oa_result_notify_sync |
SaasOrderNotify | dgj | 库存、采购关闭、OPS 订单、搜索和站点事件 |
ChannelOrderNotify | dgj_channel | 渠道人工创建、发货、退货、审核 |
StatementNotify | dgj_statement | 对账单生成和收款状态同步 |
14.4 消息不等于事务
sequenceDiagram
participant B as 业务 Service
participant DB as MySQL
participant MQ as RabbitMQ
participant C as Consumer
participant D as 下游/副作用
B->>DB: 写业务表
B->>MQ: publish
Note over DB,MQ: 多数代码不是标准 Outbox 原子提交
MQ->>C: deliver
C->>D: 执行业务
alt 成功
C-->>MQ: ACK
else 可重试异常
C-->>MQ: NACK
else 参数/业务不匹配
C-->>MQ: ACK 丢弃
end
当前代码大量采用“业务提交后直接发 MQ”或“事务中调用外部 Service”的方式。若 DB 成功而 publish 失败、MQ 成功而 DB 回滚、消费者部分成功后 NACK,都可能产生不一致。排查和设计补偿时必须明确每个动作发生在事务前、事务内还是提交后。
15. OA 审批链路
15.1 OA 回调合同
目标队列:oa_notify,routing key:oa_audit。
代表性报文:
{
"templateCode": "业务模板码",
"summaryId": "业务或OA关联标识",
"status": 2,
"reason": "审批意见"
}
OA 状态:
| 值 | 含义 |
|---|---|
2 | 审批成功 |
3 | 审批失败 |
OaNotify::oaAudit() 先按 templateCode 分流,再调用具体业务 Service:
- 授信资金池;
- 专属授信活动申请;
- 业务/费用同步;
- 授信额度调整、总额度和顾问额度申请;
- 活动申请和活动变更;
- 秒杀活动。
未知模板进入 default 后不执行任何业务,最终返回 ACK。这意味着模板配置错误可能表现为“队列消费正常但业务完全没变化”。
15.2 通用 OA 分流
flowchart TD
MQ["oa_notify / oa_audit"] --> CODE["读取 templateCode"]
CODE --> BT["授信/额度"]
CODE --> EXP["业务/费用同步"]
CODE --> ACT["活动申请/变更"]
CODE --> FLASH["秒杀 callback_20260526"]
EXP --> RESULT["sendOAResult"]
RESULT --> OAOUT["oa_result_notify"]
OAOUT --> PROVIDER["OaProvider::oaStatusSync"]
业务/费用模板不会直接在 OaNotify 调外部接口,而是发送 oa_result_notify_sync,由 OaResultNotify 调 OaProvider::oaStatusSync();异常返回 NACK。
15.3 秒杀 OA
秒杀模板码:FlashActivityEnums::OA_TEMPLATE_CODE = callback_20260526。
sequenceDiagram
participant OPS as OPS
participant F as FlashActivitySer
participant OA as OA Provider
participant MQ as oa_notify
participant N as OaNotify
participant DB as flash_activity
OPS->>F: 提交秒杀活动
F->>OA: submit(payload)
OA-->>F: id / oa_no / requestId
F->>DB: apply_status=WAIT, oa_status=WAIT, oa_no
OA->>MQ: oa_audit(templateCode, summaryId, status)
MQ->>N: oaAudit
N->>F: callback20260526(message)
F->>DB: 按 oa_no 查询活动
alt 通过
F->>DB: PASS + 按时间计算 enable_status + 商品提交状态
else 拒绝
F->>DB: REJECT + reject_reason
end
回调兼容多个字段:
- OA 号:
oa_no、oa_number、requestId、summaryId; - 结果:
status、result、auditResult; - 通过:
2、1、PASS、AGREE、SUCCESS; - 拒绝:
3、0、REJECT、FAIL。
找不到 OA 号、找不到活动或结果未知会抛异常,OaNotify 返回 NACK。
16. 销售和出库同步下游
16.1 事件字典
目标:dgj_notify。
| routing key | 触发方法 | 业务事实 |
|---|---|---|
garage_repair_order_created | syncOrderCreate() | 销售单创建 |
garage_repair_order_updated | syncOrderUpdate() | 销售单修改 |
garage_repair_order_cancelled | syncOrderCancel() | 销售单关闭 |
garage_repair_order_stopped | syncOrderStop() | 销售单结单 |
garage_repair_order_completed | 枚举保留 | 销售单完成 |
garage_repair_order_expired | 枚举保留 | 销售单过期 |
garage_repair_outbound_created | syncOutboundCreate() | 销售出库创建 |
garage_repair_outbound_updated | syncOutboundUpdate() | 出库编辑 |
garage_repair_outbound_cancelled | syncOutboundCancel() | 出库撤销 |
SyncOrderSer::publish() 每次创建 Topic Producer,发布后关闭 channel,并记录 eventType + data 日志。
16.2 销售单创建报文
{
"stationId": 10001,
"orderScene": "z_offline",
"orderType": 7,
"orderId": 123,
"orderNo": "SO...",
"orderStatus": 2,
"contactId": 456,
"contactName": "客户名称",
"totalQty": 2,
"totalAmount": "200.00",
"payMode": "CASH",
"storeId": 10,
"source": 5,
"entries": [
{
"skuId": "SKU001",
"invId": 100,
"goodsType": 1,
"isGift": 0,
"qty": 2,
"price": 100,
"amount": 200
}
]
}
当前 buildOrderEvent() 对创建和更新都固定:
orderScene=z_offline;orderType=7;orderStatus=2待出库;source=5;- 挂账映射
payMode=CHARGE,其他为CASH; - 明细金额近似零时视为赠品;
- 空
skuId视为三方物料。
虽然 Builder 定义了 ORDER_TYPE_MAP,创建报文没有按来源使用该映射,而是固定线下订单 7。如果此 Service 被 E站、活动、机器人等来源复用,下游来源分类可能失真,需核对真实调用范围。
16.3 出库报文
{
"sid": "10001",
"contactId": 456,
"outOrderNumber": 2,
"saInvoiceOrderId": 123,
"saInvoiceOrderNo": "SO...",
"saInvoiceBillNo": "XSCK...",
"submitTime": "2026-07-15 10:00:00",
"outTime": "2026-07-15 10:00:00",
"detail": [
{
"invId": "100",
"skuId": "SKU001",
"goodsOutPrice": 200,
"goodsOutNumber": 2,
"isGift": 0,
"locationId": 10,
"locationAreaId": 100
}
]
}
仓库和货位从每个明细的 qtyGrid 中提取;如果同一明细跨多个货位,循环最终只保留最后一个非零出库网格的 locationId/locationAreaId,下游无法从该报文还原多货位拆分。这是合同精度风险。
16.4 同步不应反推业务提交
flowchart LR
SAVE["销售/出库业务提交"] --> DB["业务表 + 库存"]
SAVE --> SYNC["SyncOrderSer"]
SYNC --> MQ["dgj_notify"]
MQ --> DOWN["SaaS/维修厂下游"]
DOWN -. "收到消息" .-> ACK["下游事实"]
下游没收到时先确认 DGJ 业务事实是否完成,再查同步调用、生产日志和 MQ;不要通过重做销售出库来补消息,否则可能重复扣库存。
17. 常用只读 SQL
17.1 查采购单支付状态
SELECT id, sid, billNo, billStatus, srcOrderType,
totalAmount, paymentTime, auditOpinion, createTime
FROM t_scm_po_order
WHERE billNo = 'PO...';
17.2 查采购支付扩展
SELECT id, sid, iid, billNo, amount, type,
discount, disRate, rebate, refund,
exclusive_rebate, service_amount
FROM t_scm_po_order_payinfo
WHERE billNo = 'PO...'
ORDER BY id;
注意这里金额可能仍为支付中心原始“分”单位。
17.3 查付款主单和明细
先计算:
shard = sid % 32
然后查询对应分片:
SELECT id, billNo, billType, transType, transTypeName,
buId, totalAmount, billStatus, sid, billDate, createTime
FROM t_scm_payment_{shard}
WHERE sid = 10001
AND (billNo = 'FKD...' OR id = 付款主单ID);
SELECT id, iid, billNo, transType, amount,
accId, wayId, paymentType, stlId, stlNo,
description, createTime
FROM t_scm_payment_info_{shard}
WHERE sid = 10001
AND (stlNo = 'PO...' OR iid = 付款主单ID)
ORDER BY id;
17.4 查是否重复调额
SELECT id, iid, billNo, stlNo, amount, accId, createTime
FROM t_scm_payment_info_{shard}
WHERE sid = 10001
AND stlNo = 'adjustNo';
17.5 业务库与 DWD 对账
业务库先查真实单号、业务日期、状态和数量;Hologres 使用内部报表查询工具按业务键执行只读 SQL,例如:
SELECT *
FROM dwd_bbyh_dgj_scm_po_order
WHERE station_code = '10001'
AND bill_code = 'PO...';
不要把 Hologres SQL 直接拼入生产业务接口;应通过既有 Provider 或数据平台工具执行,并记录查询时间。
18. 日志和代码检索
rg -n "支付消息通知|支付异常消息通知|价格调整单消息通知" application/controllers/tasks/PayCenterNotify.php
rg -n "sourceOrderNo|payStatus|detailTotalAmount|payAccountTypeCode" application/controllers/tasks/PayCenterNotify.php
rg -n "createTopicConsumer|createDirectConsumer|registryCallback" application/controllers/tasks
rg -n "createTopicProducer|createDirectProducer" application/Services/Mq application/Services/SyncOrder
rg -n "hologres/query|dwd_" application/Services/Report application/Providers/ReportService
rg -n "oa_audit|templateCode|callback20260526" application/controllers/tasks application/Services/Activity
rg -n "garage_repair_order_|garage_repair_outbound_" application/Services/SyncOrder
日志分类:
| 链路 | Logger/关键字 |
|---|---|
| 支付成功 | PayCenterNotify/payCenterPayResult |
| 支付异常 | PayCenterNotify/paycenterPayException |
| 调额 | cron/paycenterBalanceAdjust |
| OA | OaNotify/oaAudit, OaNotify/oaResultSync |
| 销售同步 | Services/syncData,关键字 SyncOrder发送消息 |
| 报表 | 控制器异常返回、Provider 请求日志和中心报表日志 |
19. 按现象排查
19.1 支付中心成功,采购单仍待支付
flowchart TD
A["支付成功但采购单未推进"] --> B{"PayCenterNotify 是否收到 pay_result"}
B -->|否| C["查 paycenter_notify 路由和生产端"]
B -->|是| D{"payStatus 是否字符串 02"}
D -->|否| E["当前代码 NACK,查支付中心状态"]
D -->|是| F{"sourceOrderNo 是否存在"}
F -->|否| G["核对环境和订单号"]
F -->|是| H{"原状态是否 wait_pay/pay_overtime"}
H -->|否| I["重复消息或状态已被其他流程改变"]
H -->|是| J["查事务异常、财务表和回调日志"]
19.2 采购单已支付,但没有付款单
- 确认
mainPayTypeCode是否为99零元订单;该分支有意不创建付款单。 - 用
sid % 32查正确付款分片。 - 查
t_scm_po_order_payinfo是否存在。 - 查
PaymentModel/PaymentInfoModel写入日志和事务异常。 - 对预订单按
QTZC和来源单号查询,不要只找FKD。 - 确认是否主付款单存在但某账户明细或账户流水缺失。
19.3 付款主单金额和采购支付金额差 100 倍
- 确认比较的是哪两张表。
Payment/PaymentInfo通常为元。PoOrderPayInfo的支付、返利、退款、服务费通常保留回调原始分。- 统一转换后再核对。
- 区分
detailTotalAmount是否包含服务费;主单最后会把服务费加回。
19.4 同一支付消息反复消费
- 查采购/预订单是否已被第一次回调推进。
- 查第一次是否已提交财务事务。
- 查回调是否在提交后执行任务时抛错并返回
NACK。 - 查 MQ 重试次数、重新入队和死信策略。
- 不要手工删除已落财务数据来“让重试通过”;应先确定幂等补偿方案。
19.5 报表查不到刚完成的业务单
- 查业务主表、明细和状态。
- 确认业务日期字段和查询日期范围。
- 查目标 DWD 表是否已有业务单号。
- 没有 DWD:查 ETL/同步 SLA。
- 有 DWD:复现 Service 生成的 SQL 和过滤条件。
- 检查权限、站点、分页、软删除和状态排除。
19.6 报表数量正确但金额不一致
- 对照 DWD 原始
price/amount/deduction/qty。 - 检查 DGJ 格式化公式和单位。
- 检查退货是否用负数或单独交易类型。
- 检查服务费、优惠、返利是否在不同报表分开体现。
- 检查汇总是否循环拉取了全部页,远程请求是否中途超时。
19.7 OA 显示通过,DGJ 没变化
- 查
oa_notify/oa_audit是否收到消息。 - 核对
templateCode是否精确匹配当前环境配置或枚举。 - 未匹配模板会直接 ACK,无业务动作。
- 秒杀按 OA 号查活动
oa_no。 - 检查
status是否是受支持值。 - 查
OaNotify回调日志和 NACK。
19.8 销售/出库已完成,下游没收到
- 查业务表和库存事实,确认不是主业务失败。
- 查调用点是否执行
SyncOrderSer。 - 搜索
SyncOrder发送消息日志和事件类型。 - 查
dgj_notify的 routing key、消费方和死信。 - 对照 Builder 报文字段,检查下游是否因缺字段拒绝。
- 只补发消息,不重复创建销售或出库单。
20. 当前代码风险与待确认项
20.1 库存进销存报表固定查询站点 1017
InventoryReportSer::getGoodsTravelSql() 当前有效代码固定:
where station_code=1017
使用请求 JXCSID 的代码被注释。其他服务站查询可能返回错误站点数据,这是明确的当前代码风险。修复前必须回归权限、SQL 参数化和历史依赖。
20.2 支付成功重复消息返回 NACK
当前用订单状态做幂等,已处理消息再次到达会进入“状态错误或者消息重复”异常并返回 NACK。需要确认 MQ 是否有限次重试和死信;更稳妥的合同应区分“已成功处理的重复消息”和真正失败。
20.3 details 按账户类型绑定会覆盖重复类型
array_bind_key(details, payAccountTypeCode) 把同一账户类型变成一个键。如果支付中心允许同类型多行,前面的金额可能被覆盖。需确认上游合同保证类型唯一,或改成按类型汇总。
20.4 事务提交后任务异常可能导致重复投递
TaskSer::execute() 在事务提交后、try 结束前执行。它失败会返回 NACK,但主事务已提交。必须补自动化测试和明确幂等策略。
20.5 报表 SQL 是应用拼接并发送给中心接口
输入虽然经过部分校验,但具体过滤字段仍应审计参数化和转义;中心报表接口还需要权限、超时、最大行数和 SQL 白名单保护。
20.6 报表汇总可能重复拉全量
采购和库存报表为了 sum 循环请求分页,再在 PHP 汇总。大时间范围可能产生高延迟和内存压力。导出还会全量拉取,应设置业务范围、超时和导出上限。
20.7 销售同步创建类型固定为线下单
SyncOrderBuilder::buildOrderEvent() 固定 orderType=7、source=5,没有使用已定义的来源映射。需确认该同步只用于线下 DGJ 开单,避免 E站、活动、机器人来源被错误分类。
20.8 出库多货位信息被压成一个货位
Builder 遍历 qtyGrid 时只保留最后一个非零货位。下游如果需要每个货位的数量明细,现有报文无法表达。
20.9 未知 OA 模板会静默 ACK
模板码不匹配 switch 任一分支时,回调仍 ACK。应至少有未知模板日志和告警,否则配置错误难以发现。
20.10 MqEventEnums::DEST_TYPE_RELATION 不是完整注册事实
部分消费者实际注册的 routing key 没有出现在关系数组中。排查和文档不能只读取 DEST_TYPE_RELATION,还必须检查具体消费者的 registryCallback()。
21. 改动影响面
| 修改位置 | 直接影响 | 必须回归 |
|---|---|---|
PayCenterNotify::paycenterPayResult() | 采购、预订单、授信、秒杀、账户 | 单/混合/零元/授信支付、重复、超时、回滚 |
PaymentModel/PaymentInfoModel 分片 | 所有财务查询和写入 | 32 分片、老表兼容、跨站点查询 |
AccountService::saveAccounts() | 账户余额和流水 | 收入/支出方向、撤销、混合账户 |
PoOrderPayInfoModel | 采购支付展示和优惠 | 单位、返利、退款、服务费 |
MqSer | 多业务生产者 | destination、routing key、payload、channel 关闭 |
*Notify 消费者 | 跨系统回调 | ACK/NACK、重复、缺参数、部分成功 |
Report Service | 报表查询、导出和口径 | 权限、站点、日期、分页、汇总、DWD 延迟 |
OaNotify | 授信、费用、活动和秒杀 | 所有模板分支、通过/拒绝、未知模板 |
SyncOrderBuilder | SaaS/下游订单合同 | 各来源、赠品、挂账、多货位、撤销 |
22. 回归测试清单
22.1 支付
- 普通采购单单一账户支付成功,主单、明细、账户、payinfo 和采购状态一致。
- 混合支付金额和展示类型正确。
- 授信主支付和授信作为混合明细都生成正确其他收入。
- 返利、专属返利、待退款账户和服务费单位正确。
- 零元订单只推进业务状态,不生成错误付款单。
- 预订单每个账户生成一张其他支出并推进预订单。
- 秒杀采购支付后秒杀订单正确变已支付。
- 支付失败状态、异常回调、支付超时、重复成功消息行为明确。
- 财务事务中任一写入失败全部回滚。
- 事务提交后任务失败不会重复落账。
22.2 财务分片和账户
- 代表性
sid覆盖不同sid % 32分片。 - 新系统账户创建后缓存立即可读。
- 收入为正、支出账户流水为负,撤销口径正确。
- 页面、报表和 SQL 都查询同一分片。
- 老表兼容读取不会重复统计。
22.3 报表
- 查询和导出权限分别生效。
- 日期、分页、每页 1000 上限正确。
- 当前页和汇总结果使用同一过滤条件。
- 采购、采退、入库、销售、库存代表业务单可从业务表追到 DWD。
- DWD 延迟期间页面有合理提示或排查依据。
- 大数据量导出不会无限请求、超时或内存溢出。
- 库存报表使用请求
JXCSID,不再固定站点。 - 零数量、负数退货、优惠和服务费公式有边界测试。
22.4 MQ 和 OA
- 每个消费者的 destination 和 routing key 与生产者一致。
- 成功 ACK、可重试 NACK、不可处理 ACK 丢弃均有测试和日志。
- 重复消息不会重复写业务或财务事实。
- OA 所有模板通过和拒绝分支可执行。
- 未知模板产生告警。
- 秒杀 OA 能兼容多种 OA 号和结果字段,重复回调幂等。
22.5 销售同步
- 创建、修改、关闭、结单、出库、出库撤销事件完整。
- 订单来源和类型与真实来源一致。
- 挂账/现金支付方式映射正确。
- 快准物料、三方物料、赠品字段正确。
- 多仓位/多货位出库合同满足下游需求。
- publish 失败可补发消息,不重复业务单据。
23. 代码证据索引
| 结论 | 证据文件 |
|---|---|
| 支付中心事件注册和完整回调 | application/controllers/tasks/PayCenterNotify.php |
| 支付账户类型 | application/KzData/Enums/AccountEnums.php |
| 收付款状态 | application/KzData/Enums/PaymentEnums.php |
| 付款 32 分片 | application/models/payment/PaymentModel.php, PaymentInfoModel.php, application/config/tables.php |
| 采购支付扩展字段 | application/models/orders/PoOrderPayInfoModel.php |
| MQ destination/routing key | application/KzData/Enums/MqEventEnums.php |
| 通用 MQ 生产者 | application/Services/Mq/MqSer.php |
| 订单/配送/商品/渠道消费者 | application/controllers/tasks/*Notify.php |
| OA 模板分流 | application/controllers/tasks/OaNotify.php |
| OA 结果同步 | application/controllers/tasks/OaResultNotify.php |
| 秒杀 OA 状态 | application/Services/Activity/FlashActivitySer.php, FlashActivityEnums.php |
| 报表接口和权限 | application/controllers/reports/*.php |
| Hologres 查询和 DWD | application/Services/Report/*.php |
| 报表请求校验 | application/Validate/Reports/*.php |
| 销售/出库同步生产者 | application/Services/SyncOrder/SyncOrderSer.php |
| 下游报文转换 | application/Services/SyncOrder/SyncOrderBuilder.php |
24. 仍需环境确认
- 支付中心
payOrderNo、来源订单号和回调消息 ID 的唯一性保证。 paycenter_notify对 NACK 的重试次数、退避、死信和人工补偿方式。- 付款、账户、采购状态是否始终使用同一数据库连接和事务。
- 生产中各支付金额字段的官方单位合同,尤其
PoOrderPayInfo。 - Hologres 各 DWD 表的同步频率、延迟 SLA、重跑和数据负责人。
- 中心报表 SQL 接口的鉴权、SQL 白名单、最大扫描量和超时。
- 所有 OA 模板码在各环境的实际值及未知模板告警。
- OA 和秒杀重复回调的消息唯一键与重试策略。
dgj_notify的真实消费者、消息保留、死信和手工补发入口。- 销售同步 Service 的真实调用范围是否只包含线下 DGJ 订单。
环境确认结果应写回本文或对应第 13、18、22、23 篇专项文档,不能只保留在群聊和临时排查记录中。
请求-日志-数据变更追踪卡
多入口请求链路
| 场景 | 调用方与入口 | 请求载荷/上下文 | Controller/Consumer | Service/Provider | 汇合点 | 最终业务事实 |
|---|---|---|---|---|---|---|
| 支付回调 | 支付中心 DEST_PAYCENTER_NOTIFY | payOrderNo、来源单号、payStatus、金额、支付方式 | tasks/PayCenterNotify.php | 支付、采购和活动 Service/Model | 支付单号 + 来源订单号 | 支付主明细、业务支付关系和订单状态更新 |
| OA 回调 | OA DEST_OA_NOTIFY/结果通知 | 模板码、业务 ID、审批状态、意见 | OaNotify.php、OaResultNotify.php | 模板对应业务 Service | 模板码 + 业务 ID + OA 单号 | 活动/业务审批状态推进并回传结果 |
| 业务 MQ 同步 | 销售、采购、库存、渠道等 Service | routing key、业务单号、业务快照 | MqSer 生产;各 *Notify 消费 | SyncOrderSer、领域 Service | 业务单号 + 消息业务键 | 下游系统获得创建/变更/关闭事实 |
| 报表查询 | PC 报表页面/API | sid、日期、组织、过滤条件、权限 | controllers/reports/*.php | Services/Report/* | 报表口径字段和数据日期 | 查询 Hologres/DWD,通常不写 OLTP 业务表 |
| 定时补偿 | 调度系统 | 批次、时间窗、游标/状态 | controllers/tasks/*Task.php | 领域补偿 Service | 业务主键 + task 批次 | 将漏处理记录推进到应有状态或重新发消息 |
日志证据矩阵
| 链路段 | 日志来源 | 可检索锚点 | 成功信号 | 失败信号 | 与下一段关联方式 |
|---|---|---|---|---|---|
| 支付消费 | PayCenterNotify::paycenterPayResult | payOrderNo、来源单号、消息 ID、payStatus=02 | 支付/业务关系落库并 ACK | 非成功态、金额不符、重复、NACK | 支付单号查 SCM_PAYMENT* 和来源关系 |
| OA 消费 | OaNotify/OaResultNotify | template code、business ID、OA 单号 | 命中模板并推进业务状态 | 未知模板、找不到业务、回调乱序 | 模板码 + 业务 ID 查业务主表 |
| MQ 生产/消费 | MqSer 与目标 *Notify | destination、routing key、业务单号、消息 ID | 生产成功且消费者 ACK | 发送失败、NACK、死信/重复 | 同一业务键贯穿生产、消费和业务表 |
| 报表请求 | reports Controller/Service/数据源日志 | 报表方法、sid、日期范围、查询 ID | 返回行数和数据日期明确 | 权限拒绝、超时、数据源延迟 | 查询条件与 DWD 最大数据日期对照 |
| 补偿任务 | 具体 Task 方法日志 | task 名、批次、开始结束时间、业务 ID | 扫描/成功/跳过/失败数可对账 | 任务中断、失败数增长、重复副作用 | 批次业务 ID 回查主表和消息记录 |
环节数据变更台账
| 步骤 | 代码位置 | 事务 | 读取事实 | 写入表/缓存/MQ | 字段或数量变化 | 回查证据 |
|---|---|---|---|---|---|---|
| 接收支付结果 | PayCenterNotify | 单消息本地事务 | payStatus、来源订单、既有支付单 | SCM_PAYMENT、SCM_PAYMENT_INFO、来源业务支付表 | 首次成功 insert/累计;重复成功不得重复记账;订单状态按来源推进 | payOrderNo 唯一性、金额合计、来源单状态 |
| 账户/应收核销 | 财务 Service | 财务事务 | 应付/应收余额和账户余额 | SCM_ACCOUNT、SCM_ACCOUNT_INFO、SCM_REPAYMENT_ORDER 等 | 余额和已核销金额按分录增减,借贷/来源总额保持平衡 | 业务单、分录号、前后余额 |
| OA 状态推进 | OaNotify -> 模板业务 Service | 消费事务;OA 外部系统已在边界外 | 当前业务状态、审批结果 | 业务主表、OA 结果 MQ | status: pending -> approved/rejected,旧/重复结果不回退 | OA 单号、模板码、状态更新时间 |
| 生产下游消息 | MqSer、SyncOrderSer、SyncOrderBuilder | 常在业务 commit 后 | 已提交订单/出库/库存事实 | dgj_notify 等 destination | 本地业务事实 -> 消息快照;不直接改变本地业务状态或仅记同步标记 | routing key、单号、生产结果、消费 ACK |
| 报表查询 | reports/* -> Services/Report/* | 只读,无 OLTP 写事务 | Hologres/DWD 数据、权限和筛选条件 | 通常无业务表写入 | 数据不变;仅聚合、分组、分页和口径转换 | SQL/查询 ID、最大数据日期、抽样单据 |
| 补偿执行 | tasks/*Task.php | 每条/每批事务由具体任务决定 | 待支付、待同步、超时或不一致记录 | 业务表、MQ、缓存 | 仅把满足条件的记录推进/重发;已完成记录跳过 | 执行前后状态、影响行数、重复运行零副作用 |
子模块级独立追踪
子模块追踪:pay-success 支付成功落账
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 消费 | DEST_PAYCENTER_NOTIFY 成功事件 | message ID、payOrderNo、来源单号、payStatus=02、金额 | application/controllers/tasks/PayCenterNotify.php::paycenterPayResult | 来源订单、当前支付状态、已有支付主明细/关系 | 回调事务 insert/update SCM_PAYMENT/INFO 和来源支付关系;paid amount old -> old+n,来源状态推进 | message ID + payOrderNo + source billNo | 非 02 不按成功落账;同 payOrderNo 重复消费 0 增量 |
| 来源联动 | 支付记录 commit 前后按来源分流 | payOrderNo、source type/id、业务单号 | application/controllers/tasks/PayCenterNotify.php -> 采购/活动/授信 Service | 来源当前态和是否已处理 | 本地事务或后置边界将 order/status/locked/used old -> new | payOrderNo + 来源单 + Service 方法 | 支付已落账、来源失败只补来源动作,不重复支付分录 |
子模块追踪:pay-exception 支付异常与退款回调
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 异常事件 | PayCenter exception event | message ID、payOrderNo、source bill、exception type | application/controllers/tasks/PayCenterNotify.php 对应 handler | 原支付、来源订单状态和异常是否已登记 | 单消息事务异常/支付状态 old -> failed/exception 或 insert 异常记录 | event + payOrderNo + source bill + message ID | 未知类型告警;不能因异常消息直接回滚已确认成功支付 |
| 退款 | refund result event | refund order、original payOrderNo、amount、status | application/controllers/tasks/PayCenterNotify.php -> Refund/Exclusive Service | 原成功支付、已退金额、业务退货/关闭事实 | 退款事务 refunded old -> old+n,账户/额度按合同 old -> old+n;重复为 0 | original/refund pay IDs + source bill | 外部成功本地失败按 refund ID 补账;普通退款与专属授信分支分开核对 |
子模块追踪:credit-adjust 额度调整与授信回调
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 调整回调 | PayCenter/额度事件 | message ID、adjust ID、station/account、source order、amount/type | application/controllers/tasks/PayCenterNotify.php -> ExclusiveBaitiao/Account Service | 可用、冻结、已用额度和原业务流水 | 额度事务按类型 available/frozen/used old +/- n,insert 调整流水 | adjust ID + source order + station + message ID | 同 adjust/source key 只记一次;余额不足或上下级不守恒 rollback |
| 关闭返还 | 订单关闭/退款联动 | order/pay/refund ID、release amount | application/controllers/tasks/OrderCenterNotify.php / PayCenterNotify -> 授信 Service | 原占额、已返额、外部账本最终态 | frozen/used old -> old-n、available old -> old+n;不超过原占用 | original order/pay + adjust ledger ID | 外部本地非原子时只补差额,不能新建无原单的额度调整 |
子模块追踪:report-query 报表查询与离线数据
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 查询 | /reports/* 页面/API | request ID、sid/组织、日期、状态、分页、report name | application/controllers/reports/*.php -> application/Services/Report/* -> Provider | 权限、Hologres/DWD 数据和最大数据日期 | 只读,不写 OLTP;原始明细 -> 过滤/聚合/分页结果 | request/query ID + report method + date/sid | 空/少数据先查权限、口径和数据日期,不以报表结果反改业务表 |
| 导出/对账 | 报表导出或抽样核对 | export/query ID、同列表筛选、business IDs | Report Service -> Export;OLTP 只读查询 | 列表主键集合、源业务主明细 | DB 不变;生成文件,或比较 DWD 与 OLTP 快照 | export ID + query condition hash + sample billNo | 页面/导出不一致用同条件比主键集合;同步延迟交 ETL 重跑,不重做业务单 |
子模块追踪:oa-callback OA 模板分流与结果回调
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| OA 通知 | DEST_OA_NOTIFY | message ID、template code、business ID、OA form、result | application/controllers/tasks/OaNotify.php -> 模板对应 Service | 模板映射、业务当前审批态、回调是否已处理 | 单消息事务 status pending -> approved/rejected,结果/意见写入 | template + business ID + OA form + message ID | 未知模板/找不到业务告警;旧回调不得回退终态 |
| 结果回传 | 本地业务结果发 OA/消费结果队列 | business ID、OA form、result event | application/controllers/tasks/OaResultNotify.php / application/Services/Mq/MqSer.php | 本地已提交审批结果 | 本地 DB 通常不写或同步标记 old -> sent;MQ/外部为异步边界 | business/OA ID + routing key + message ID | 发送失败只补 OA 结果消息,不重复执行业务审批 |
子模块追踪:order-sync 销售和出库下游同步
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 构建发送 | 销售/出库/撤销 commit 后 | sale/invoice billNo、source type、event/version | application/Services/SyncOrder/SyncOrderSer.php -> SyncOrderBuilder.php -> MqSer | 已提交主明细、状态、数量、金额和来源 | 本地核心表 DB 不变;构造 dgj_notify 消息,派生状态无 -> 待消费 | billNo + routing key + message ID + payload version | 发送失败按业务键补发;不重做销售/出库事务 |
| 下游确认 | App/SAAS 消费或回调 | message ID、business bill、downstream status | 下游消费者;本地回调在 application/controllers/tasks/*Notify.php | 下游当前态和消息版本 | 下游 old -> new,本地仅按合同写同步状态 | 两端用 billNo/source order 关联 | ACK 丢失重复消息零副作用;下游已成不因本地重试再建单 |
子模块追踪:mq-compensation MQ 消费与定时补偿
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 消费 | application/controllers/tasks/*Notify.php | destination、event、message ID、稳定业务键 | Consumer -> 领域 Service/Provider | 消息版本、当前业务状态、幂等事实 | 单消息事务 status/qty/amount old -> new;commit 后 ACK | destination + event + business key + message ID | 业务异常 NACK/告警;已完成重复消息 ACK 且 DB 不变 |
| 补偿任务 | application/controllers/tasks/*Task.php CLI | task、batch、time window、cursor、business ID | Task -> 领域幂等方法 / MqSer | 待处理状态和支付/订单/外部最终事实 | 每条事务仅把差异 old -> expected 或重发 MQ;已完成跳过 | task/batch + business IDs + scan/success/skip/fail counts | 小批、可续跑;补偿前取 before 快照,重跑必须 0 副作用 |