本文回答的不是“报表代码放在哪里”,而是“某个数字为什么是这个值、它何时进入报表、页面与导出为什么可能不同、应当沿哪条链路核对”。
已直接核验 DGJ2 当前代码;报表中心内部 ETL、DataService 实际物理表和生产同步 SLA 不在本仓库中,统一标为 待外部系统确认。

1. 业务目标

DGJ2 报表横跨业务库、分表、DWD/Hologres、独立报表服务和数据服务。一个“金额不一致”可能来自五类差异:

  1. 业务含义不同:订单金额、出库金额、应收金额、实收金额不是同一个指标。
  2. 时间口径不同:创建时间、业务日期、出库日期、入库日期、月份不同。
  3. 状态口径不同:草稿、关闭、取消、退货、部分收款是否纳入不同。
  4. 数据源时效不同:业务库已更新,离线 DWD 尚未同步。
  5. 展示层计算不同:列表、汇总行、导出模板可能再次计算或格式化。

本文把采购、销售、库存、利润、应收、应付和对账单放在同一套核对框架下。

2. 什么时候查本文

问题现象首先定位不要先做什么
页面查不到刚完成的单据数据源、业务日期、ETL 延迟直接改业务单状态
页面与导出条数不同是否复用同一 where、导出是否循环全部分页只看 Excel 行数
毛利或毛利率不对折后应收、出库成本、分母和负毛利规则用订单含税金额直接相减
应收余额不对期初、销售/退货、收款/撤销、历史欠款只查收款主表
对账单与实时应收不同对账单生成区间、快照时间、是否重新生成直接覆盖对账单明细
采购跨转站历史缺失old_sid、transfer_sid_time、180 天窗口只按当前 sid 查
库存流水显示其他站点固定站点条件风险先补业务库存

3. 报表事实卡

维度当前实现
采购订单/退货/入库主要查询 DWD/Hologres
销售进度/商品/客户主要调用独立 REPORT_SERVICE
销售对账代码中同时保留 Hologres 与报表服务实现
库存流水查询 Hologres,但当前入口存在代码风险
利润表调用 DataService ReportProvider
应收/应付查询 DGJ 本地业务表、历史欠款和分表
对账单本地生成业务快照,独立保存明细、物料、任务和发送记录
Hologres 公共入口CENTER_API/devcenter/report/hologres/query
Hologres 默认批次1000 行
Hologres 默认超时5 秒
销售 Provider 单页上限2000 行

4. 整体数据架构

flowchart LR
    A[业务操作] --> B[(DGJ 业务库与分表)]
    B --> C[同步或 ETL]
    C --> D[(DWD / Hologres)]
    C --> E[独立报表服务]
    C --> F[DataService]
    B --> G[本地应收应付服务]
    B --> H[对账单生成服务]
    D --> I[采购和库存类页面]
    E --> J[销售类页面]
    F --> K[利润类页面]
    G --> L[往来账页面]
    H --> M[对账单页面与发送]

4.1 最重要的判断

报表数字不是业务事实本身,而是“业务事实 + 口径规则 + 数据同步时点”的结果。

排查前必须写出四元组:

报表名称 + 指标名称 + 查询区间 + 数据源时点

例如:

销售明细报表 + 毛利 + 2026-07-01 至 2026-07-15 + 报表服务当前返回

5. 入口文件地图

业务ControllerService / Provider
采购报表application/controllers/reports/PurchaseReport.phpapplication/Services/Report/PurchaseReportSer.php
销售报表application/controllers/reports/SaleReport.phpapplication/Services/Report/SaleReportSer.php、application/Providers/ReportService/SaleProvider.php
库存报表application/controllers/reports/InventoryReport.phpapplication/Services/Report/InventoryReportSer.php
利润报表application/controllers/reports/ProfitReport.phpapplication/Services/Report/ProfitReportSer.php、application/Providers/DataService/ReportProvider.php
客户报表application/controllers/reports/CustomerReport.phpapplication/Services/Report/CustomerReportSer.php
应收由对应报表 Controller 调用application/Services/Report/AccountReceiveSer.php
应付由对应报表 Controller 调用application/Services/Report/AccountPaySer.php
对账单StatementBill.php、StatementSend.php、StatementStrategy.phpapplication/Services/Statement/*
公共 Hologres-application/Services/Report/ReportBaseSer.php

6. 数据源与物理表字典

常量或逻辑名物理表 / 服务主要用途
PODETAILTABLEdwd_bbyh_dgj_scm_po_order采购订单明细
PURETURNTABLEdwd_bbyh_dgj_scm_po_order_return采购退货明细
PUDETAILTABLEdwd_bbyh_dgj_scm_pu_invoice采购入库明细
ARREARSTABLEdwd_dgj_home_arrears首页应收应付余额
PUOUTTABLEdwd_dgj_out_bound出库与微仓补货统计
ACTIVITY_GIFT_TABLEdwd_dgj_activity_gift赠品活动订单
ACTIVITY_NEW_SID_TABLEdwd_station_activity_is_new_order上新活动服务站
销售历史dwd_dgj_sale_history历史销售相关统计
销售对账dwd_bbyh_dgj_sa_invoice_payment_info出库与收款对账
库存流水dwd_bbyh_dgj_scm_inventory_all库存出入与期末数量
销售报表服务sale/schedule*、sale/reconciliation销售明细及聚合
利润数据服务ReportProvider 三类数据主营收入成本、其他收入、其他成本

注意:DWD 表是报表层事实表,不应反向直接修改。业务修复应落在业务表和正规补偿链路,等待同步后再核对 DWD。

7. 通用时间口径

字段常见含义适用问题
create_time / createTime记录实际创建时间判断事件先后、回调何时落库
dw_billdate报表业务日期采购、销售、库存报表区间筛选
billDate本地业务单据日期应收应付流水展示与归属
dw_month月度数据时间戳利润表按月归属
start_time / end_time对账单快照覆盖区间对账单生成与重生成

7.1 统一排查原则

flowchart TD
    A[用户给出查询日期] --> B{报表 where 用哪个字段}
    B -->|dw_billdate| C[核对业务日期]
    B -->|create_time| D[核对落库时间]
    B -->|billDate| E[核对单据日期]
    B -->|dw_month| F[核对月份时间戳]
    C --> G[再判断区间边界是否含首尾]
    D --> G
    E --> G
    F --> G

不能用“这张单是今天创建的”证明它一定属于今天报表,必须看报表实际筛选字段。

8. 通用数量与金额符号

业务动作数量通常方向余额通常方向
采购入库正数应付增加
采购退货负向业务应付减少
销售出库正向销售量应收增加
销售退货负向销售量应收减少
收款不改库存应收减少
收款撤销 / 退款不改库存应收恢复或增加
库存入库rk_qty 增加期末库存增加
库存出库ck_qty 增加或事实表中以负数表达期末库存减少

具体正负号以 DWD 字段定义为准;核对时不要同时“使用负数”又“手工再减一次”。

9. 页面、汇总与导出三层口径

flowchart LR
    A[筛选参数] --> B[构造 where / SQL]
    B --> C[查询当前页]
    B --> D[循环查询全部页]
    C --> E[页面 rows]
    D --> F[汇总 sum]
    D --> G[导出模板]
    G --> H[数字格式化与字段重命名]

页面与导出一致,至少需要同时满足:

  1. 复用同一套筛选构造方法。
  2. 导出循环分页没有漏页或重复页。
  3. 页面汇总不是只汇总当前页。
  4. 模板没有再次改变符号或公式。
  5. 查询期间数据没有继续变化。

10. Hologres 公共调用流程

ReportBaseSer 固定向报表中心提交 JSON:

{
  "sql": "select ... from dwd_table where ...",
  "offset": 0,
  "size": 1000
}

业务流程:

sequenceDiagram
    participant C as Report Controller
    participant S as Report Service
    participant P as PurchaseProvider
    participant H as Hologres Query API
    C->>S: 查询参数 + 当前登录站点
    S->>S: 生成 countSql 和 dataSql
    S->>P: count 请求
    P->>H: POST SQL/offset/size
    H-->>P: 总条数
    S->>P: 当前页或循环全部页
    P->>H: POST SQL/offset/size
    H-->>S: DWD 结果
    S->>S: 字段格式化和汇总
    S-->>C: records/rows/sum

公共响应结构:

字段含义
records总记录数
page当前页
total总页数
limit页大小
rows当前页数据
sum全量汇总或服务计算结果

11. 采购报表入口与权限

Controller 方法用途查询权限导出权限
purchaseOrderDetailReport快准采购订单明细PUREPORTPUR_QUERY,总部管理员例外KZ_ORDER_QUERY_EXPORT
puOrderReturnReport采购退货明细PURRETURN_QUERY当前代码同为 PURRETURN_QUERY
puOrderDetailReport采购入库明细PUREOORTDETAIL_QUERYPUREOORTDETAIL_EXPORT
puOrderDetailGoodsReport按商品汇总入库PUREPORTINV_QUERYPUREPORTINV_EXPORT
puOrderDetailSupplyReport按供应商汇总入库PUREPORTPUR_QUERYPUREPORTPUR_EXPORT

11.1 请求示例

以下是 Controller 接收的业务参数形态;登录态中的 JXCSID 由基础 Controller 注入,不应由普通用户任意覆盖:

POST /reports/PurchaseReport/purchaseOrderDetailReport
Content-Type: application/x-www-form-urlencoded

beginDate=2026-07-01&endDate=2026-07-15&page=1&rows=50&brandId=&require=

关键参数:

参数含义
beginDate、endDate按 dw_billdate 筛选
page、rows当前页与页大小
categoryTreeAllValue分类节点,后端展开子分类
brandId品牌编码集合
require / skey商品、单号、品牌、规格或 SKU 模糊匹配
storageNo货位集合,主要用于入库报表
customerId / customerNo供应商筛选

12. 采购订单明细主流程

flowchart TD
    A[接收日期和筛选项] --> B[读取服务站 old_sid 与转站时间]
    B --> C[确定当前站 / 老站 / 两站联合]
    C --> D[限制最早可读日期]
    D --> E[拼接 dw_billdate 和分类品牌搜索条件]
    E --> F[应用商品数据过滤]
    F --> G[查询 DWD 采购订单明细]
    G --> H[当前页展示]
    G --> I[循环全部页计算汇总或导出]

12.1 核心字段

字段含义
bill_code采购单号
sku_idSKU
src_company_product_code快准商品编码
qty采购数量
outqty发货/出库数量
waitqty待发数量
inqty已入库数量
closeqty已关闭数量
price单价
amount明细金额
deduction优惠/抵扣
po_order_info_id采购明细标识,也是默认排序依据

12.2 转站历史规则

采购订单明细不是简单按当前 JXCSID 查询:

查询区间与转站时间关系站点条件
全部早于转站老 sid
全部晚于转站当前 sid
跨越转站当前 sid + 老 sid
老站历史过早最早限制为转站时间前 180 天

如果整个查询区间早于允许读取的最早日期,服务直接返回空结果。

排查意义:同一服务站迁移前后的采购数据缺失,先核对 old_sid 与 transfer_sid_time,不要先判断 ETL 丢数。

13. 采购金额口径

采购订单明细保存数量、单价、金额和抵扣。采购入库相关展示代码还会按实际入库数量分摊抵扣:

入库金额 = price × inqty - deduction × inqty ÷ qty

含义:订单整行优惠按“本次/累计入库数量占采购数量比例”分摊。

13.1 边界风险

当 qty = 0 时该公式存在除零风险。正常业务应保证采购数量大于零,但历史修复、退货转换或异常 DWD 数据可能打破前提。

核对时同时取:

qty, inqty, price, amount, deduction

不要只比最终金额。

14. 采购退货口径

退货报表来自 dwd_bbyh_dgj_scm_po_order_return,主要字段包括:

字段用途
sa_code关联销售/来源单号,存在时优先展示
bill_code采购退货单号
bill_status / bill_status_name状态值与展示名
trans_type_name交易类型名
qty、price、amount数量、单价、金额

状态筛选有一条兼容规则:前端选择状态 1 时,SQL 使用 bill_status in (1,2),把旧版本“待审核”和“审核中”合并为同一展示口径。

flowchart LR
    A[前端状态=待审核] --> B[后端兼容映射]
    B --> C[bill_status IN 1,2]
    C --> D[统一展示待审核相关记录]

15. 采购入库口径

入库明细来自 dwd_bbyh_dgj_scm_pu_invoice,按 dw_billdate、服务站、货位、供应商、商品、品牌和分类筛选。

三种视图的粒度不同:

视图分组粒度适合回答
入库明细入库单明细哪一张单、哪个货位入了多少
按商品汇总商品 / SKU某商品期间总入库
按供应商汇总供应商某供应商期间供货金额和数量

明细与汇总不能直接按“行数”比较,必须把明细聚合到相同粒度后再比较。

16. 采购报表排查 SOP

1. 固定 sid、beginDate、endDate 和完整筛选参数。
2. 记录 Controller 方法和页面返回 records/sum。
3. 查业务采购主单、明细、入库或退货单状态。
4. 确认报表按 dw_billdate,不是业务表 createTime。
5. 有转站历史时核对 old_sid 与 transfer_sid_time。
6. 查询对应 DWD 是否已有记录。
7. 对 qty/inqty/closeqty/deduction 逐字段核算。
8. 使用同一条件导出,比较主键集合而非只比较总金额。

17. 销售报表服务入口

销售进度类报表通过 SaleProvider 调用独立服务:

服务路径用途
sale/schedule销售明细 / 进度
sale/scheduleGoods按商品汇总
sale/scheduleContact按客户汇总
sale/reconciliation销售对账

Provider 单页最多取 2000 条;全量汇总和导出需要循环所有分页。

17.1 销售查询条件

packScheduleWhere 生成的关键过滤:

条件报表字段
当前站点station_code
日期范围dw_billdate between
客户cust_code in (...)
商品goods_id in (...)
货位location_id in (...)
品牌brand_code in (...)
分类展开分类 path 后使用 category_id in (...)
销售员先按员工编号查内部 ID,再过滤 sales_id

18. 销售明细金额公式

销售报表不是只看一个 amount:

指标公式 / 字段
原价金额goods_price × total_num
优惠金额deduction
折后应收dis_amount
成本金额cost_price × total_num
毛利dis_amount - cost_price × total_num
毛利率毛利 ÷ dis_amount × 100%,分母为 0 时返回 0
flowchart LR
    A[销售数量 total_num] --> B[原价金额]
    C[商品单价 goods_price] --> B
    B --> D[减优惠 deduction]
    D --> E[折后应收 dis_amount]
    A --> F[成本金额]
    G[单位成本 cost_price] --> F
    E --> H[毛利]
    F --> H
    H --> I[毛利率]
    E --> I

18.1 负毛利历史风险

部分汇总代码使用“毛利大于 0 才计算毛利率”的条件。结果是负毛利场景可能显示 0%,而不是负毛利率。

这不是数学口径,应在改动前确认产品定义:

数学毛利率 = 负毛利 / 正应收 = 负百分比
当前部分实现 = 毛利 <= 0 时显示 0

修复时必须同时回归页面行、合计行和导出。

19. 销售聚合口径

按商品聚合的核心方式:

销售数量合计 = Σ total_num
原价金额合计 = Σ(total_num × goods_price)
优惠合计 = Σ deduction
折后应收合计 = Σ dis_amount
成本金额合计 = Σ(cost_price × total_num)
毛利合计 = 折后应收合计 - 成本金额合计

总毛利率必须用“总毛利 ÷ 总应收”,不能平均每行毛利率。

示例:

行应收成本毛利率
A1005050%
B90081010%
正确合计100086014%

简单平均得到 30%,是错误口径。

20. 销售对账口径

销售对账明确排除已取消出库:

bill_status <> SaOrderEnums::OUT_BILLSTATUS_CANCELED

收款状态由剩余金额判断:

前端选择条件业务含义
不欠款res_amount = 0已全部结清
欠款res_amount <> 0仍有余额;也要关注负余额异常

默认查询区间:结束日默认为当天,开始日默认为结束日所在月的 1 日。

flowchart TD
    A[销售出库事实] --> B{是否取消}
    B -->|是| C[排除]
    B -->|否| D[计算应收与已收]
    D --> E[res_amount]
    E -->|=0| F[不欠款]
    E -->|不等于0| G[欠款或异常余额]

21. 销售列表与导出一致性

列表和导出总体复用 Provider 与筛选条件,但导出还会:

  1. 循环全部分页。
  2. 将金额执行 number_format。
  3. 映射展示名称。
  4. 在模板中生成合计行。

因此核对顺序应为:

where 条件 -> 原始主键集合 -> 原始数值 -> Service 汇总 -> 导出格式

不要拿格式化后的字符串直接做数值比较,例如 1,000.00 与 1000。

22. 销售报表排查 SOP

1. 确认查的是销售订单、销售出库还是销售对账。
2. 固定 station_code 和 dw_billdate 区间。
3. 核对取消状态是否被排除。
4. 分别取 total_num、goods_price、deduction、dis_amount、cost_price。
5. 手算单行与全局毛利,不平均行毛利率。
6. 查 REPORT_SERVICE 返回的 total、rows 和分页。
7. 对比页面与导出的业务主键集合。
8. 若业务库有而报表服务无,转查同步链路和延迟。

23. 库存流水报表

库存流水计划查询 dwd_bbyh_dgj_scm_inventory_all,目标字段包括入库、出库和期末数量。

理论链路:

flowchart LR
    A[库存业务流水] --> B[ETL]
    B --> C[dwd_bbyh_dgj_scm_inventory_all]
    C --> D[count 查询]
    C --> E[当前页查询]
    C --> F[sum 查询]
    D --> G[页面]
    E --> G
    F --> G

23.1 当前代码审计发现

InventoryReportSer 存在以下高风险点,必须先在环境确认入口是否仍启用:

风险当前实现表现影响
固定站点条件动态 JXCSID 条件被注释,使用固定站点常量可能查到错误站点或所有用户看到相同数据
汇总被覆盖先执行 sumSql,随后又用全量明细覆盖 $sum汇总结果语义改变
方法归属异常调用 handlePurchaseDetailData,本类和父类未定义该方法入口运行时可能报错
汇总字段可疑sum(qm_qty) 无别名,同时还有 sum(qm) as qm_qtys响应字段不稳定
权限复用Controller 可能复用采购报表权限权限语义不清晰

这部分不能按“正常功能”写入使用说明,应按待修复代码处理。

23.2 正确修复边界

1. 恢复 station_code = 当前 JXCSID,并验证总部账号选站逻辑。
2. 明确列表字段与汇总字段,保留 sumSql 的单行结果。
3. 为库存流水实现专用 handle 方法。
4. 明确 rk_qty、ck_qty、qm_qty 的正负号和单位。
5. 新增两个不同 sid 的隔离测试。
6. 回归空数据、单页、多页和导出。

禁止只把固定站点替换掉就上线;后续汇总与方法调用仍可能失败。

24. 利润表数据流

利润表通过 ReportProvider 并行/异步取得三组数据:

类型Service 常量金额字段
主营业务收入与成本coreBizsale_fee、cost_fee
其他业务收入otherIncomeqtsr_fee
其他业务成本otherCostqtzc_fee
sequenceDiagram
    participant C as ProfitReport
    participant S as ProfitReportSer
    participant D as DataService ReportProvider
    C->>S: sid + startMonth + endMonth
    par 主营业务
        S->>D: getCoreBizFinReport
    and 其他收入
        S->>D: getOtherIncomeReport
    and 其他成本
        S->>D: getOtherCostReport
    end
    D-->>S: 按 dw_month 返回数据
    S->>S: 按月归组并计算利润
    S-->>C: 月份列表 + 合计

dw_month 是毫秒时间戳,Service 转成 Y-m。金额字段先取绝对值并保留两位小数。

25. 利润公式

指标公式
主营利润主营收入 - 主营成本
主营利润率主营利润 ÷ 主营收入 × 100%;主营收入不大于 0 时为 0
其他利润其他收入 - 其他成本
总利润主营利润 + 其他利润

合计行重新用所有月份总收入和总成本计算,属于加权后的全局利润率,不是月利润率的算术平均值。

25.1 请求示例

POST /reports/ProfitReport/getProfitReport
Content-Type: application/x-www-form-urlencoded

startMonth=2026-01&endMonth=2026-07

总部账号可以通过 storeId 选择服务站,构造函数会把有效 storeId 写入本次查询的 JXCSID。普通站点使用登录站点。

25.2 权限边界

页面入口 init、initDetail 检查 PROFIT_REPORT。查询和导出方法自身未看到同样的显式权限调用,依赖公共 Controller、路由或页面访问链路。

因此需增加接口级验证:未授权登录用户直接请求查询/导出方法时必须被拒绝。

26. 利润明细展示规则

明细中:

原始值展示值
cust_type = repair_factory客户
其他 cust_type服务站
profit_percent原值 × 100 后追加 %
profit_rate原值 × 100 后追加 %

如果 DataService 已返回百分数而不是小数,再乘 100 会放大百倍。接口契约必须明确 0.15 表示 15%,不能只靠字段名猜。

27. 应收账款口径

应收使用本地业务数据构造“期初 + 期间事件 + 运行余额”。

27.1 期初余额组成

概念公式:

期初应收
= 期初前销售出库
- 期初前收款
- 期初前收款优惠
+ 客户扩展表历史欠款 debt_amount
+ 期初前导入的客户欠款调整(明细实现包含)

27.2 期间余额变化

运行余额
= 销售出库
- 销售退货
- 收款
- 收款优惠
+ 收款撤销或退款恢复的应收
+ 客户欠款调整

具体事件方向由 transType 和来源类型转换,页面会把销售交易标为“销售出库”“销售退货”等。

flowchart TD
    A[计算期初余额] --> B[查询期间销售/销退]
    A --> C[查询期间收款/撤销]
    A --> D[查询客户欠款调整]
    B --> E[合并事件]
    C --> E
    D --> E
    E --> F[按 createTime 排序]
    F --> G[逐笔更新运行余额]

27.3 已发现的明细/汇总差异

AccountReceiveSer 的明细路径会把 ContactDebt 事件合入;总账路径存在注释,说明该金额与 ContactExt.debt_amount 重复,因此没有再次加到期初。

这意味着:

  1. 两种方法对历史欠款的语义不同。
  2. 数据迁移方式不同的客户可能出现明细与汇总不一致。
  3. 修复前要识别 debt_amount 是余额快照还是导入事件累计。

不能简单取消注释或双边都相加,否则可能重复计入。

28. 应付账款口径

应付同样采用事件流:

期初应付
= 期初前采购入库
- 期初前付款
+ 供应商扩展表 debt_amount

期间运行余额:

运行余额 += 采购入库金额 - 采购退货金额 - 付款金额 + 付款撤销影响

代码把采购类事件与付款事件合并后按 createTime 排序,再逐笔计算余额。

28.1 时间双口径

往来账展示使用 billDate,事件排序使用 createTime。因此同一业务日内后补录或回调延迟的记录,展示日期相同但运行余额顺序可能不同。

出现“日末余额正确、日内顺序看起来不对”时,应比较两个字段。

29. 往来账分页与截断风险

部分底层查询调用使用固定 limit=500、offset=1 形态。是否由 Model 内部继续遍历,需要结合实际方法确认。

高频客户或供应商在大区间内超过 500 条时,必须验证:

业务库事件总数 == Service 合并前事件数 == 页面/导出明细数

如果不等,先排查分页截断,不能用“余额碰巧相等”证明完整。

30. 对账单不是实时查询

对账单将指定期间的业务事实生成并保存为独立快照:

表常量物理表作用
T_BS_STATEMENT_BILLt_bs_statement_bill_v2,按 64 分片对账单主表
T_BS_STATEMENT_BILL_DETAILt_bs_statement_bill_detail_v2业务明细
T_BS_STATEMENT_BILL_DETAIL_ITEMt_bs_statement_bill_detail_item_v2商品物料明细
T_BS_STATEMENT_BILL_TASKt_bs_statement_bill_task_v2自动生成任务
T_BS_NEED_STATEMENT_USERt_bs_need_statement_user需要对账的客户
T_BS_STATEMENT_STRATEGYt_bs_statement_strategy_v2日/月策略
T_BS_STATEMENT_STRATEGY_RELATIONt_bs_statement_strategy_relation_v2策略与客户关系
T_BS_STATEMENT_SEND_RECORDt_bs_statement_send_record_v2发送记录

30.1 对账单汇总字段

字段含义
begin_receivable期初应收
sales_amount期内销售
return_amount期内退货
payment_amount期内收款
actual_payment期内实收
end_receivable期末应收
end_receivable_uppercase期末应收大写金额

基本关系应满足:

期末应收 = 期初应收 + 期内销售 - 期内退货 - 期内收款 ± 其他明确调整

若 payment_amount 与 actual_payment 同时存在,需确认优惠、抹零或核销差额如何表达,不能把二者同时全额扣减。

31. 对账单状态机

stateDiagram-v2
    [*] --> 生成任务
    生成任务 --> 未执行
    未执行 --> 执行中
    执行中 --> 成功
    执行中 --> 失败
    失败 --> 未执行: 重试或补偿
    成功 --> 草稿账单
    草稿账单 --> 已核对
    已核对 --> 待发送
    待发送 --> 发送中
    发送中 --> 发送成功
    发送中 --> 发送失败
    发送失败 --> 待发送: 重试,最多受策略限制

状态值:

对象状态
生成任务0 未执行、1 执行中、2 成功、3 失败
对账单0 草稿、1 已核对
收款状态0 未收款、1 部分收款、2 已收款
发送记录1 待处理、2 处理中、3 成功、4 失败、5 无需发送
机器人发送0 未发送、1 已发送、2 失败
策略0 禁用、1 启用

默认发送最大重试次数常量为 3。

32. 对账单业务类型

值类型
1普通销售
2普通铺货
3微仓铺货
4销退(人工)
5销退(自动)
6销售收款
7销售退款

来源还区分 pay 收款核销和 pay-cancel 收款撤销。漏掉撤销会让历史对账单与实时应收方向相反。

33. 对账单生成与发送流程

sequenceDiagram
    participant T as 定时任务/人工操作
    participant ST as StrategyService
    participant TS as StatementTaskSer
    participant SS as StatementSer
    participant DB as 对账单分表
    participant SEND as SendService/MQ
    T->>ST: 读取启用策略和客户关系
    ST->>TS: 生成唯一任务
    TS->>SS: 按客户与期间生成账单
    SS->>DB: 写主表、明细、物料和汇总
    DB-->>T: 草稿或已生成
    T->>SS: 人工核对
    SS->>DB: 状态改为已核对
    T->>SEND: 手动或自动发送
    SEND->>DB: 写发送记录
    SEND->>SEND: MQ 处理与失败重试

33.1 为什么对账单与实时应收不同

原因说明
快照时间不同对账单生成后又发生收款、退货或撤销
账期不同页面查询日期与账单 start_time/end_time 不一致
业务类型配置不同策略可控制普通销售、铺货、微仓是否纳入
生成失败或漏任务客户未进入 need_statement_user 或任务失败
重生成前后不同旧快照未包含后补数据
分表定位错误未按 sid % 64 查到对应主表/明细

正确动作是先比较生成时点和来源明细,再决定是否允许重生成;不能直接改汇总金额。

34. 报表权限核对

报表通常存在三种权限:

  1. 页面菜单权限。
  2. 查询接口权限。
  3. 导出和成本字段权限。

销售报表还可能单独控制成本价和毛利字段。总部账号可能跳过站点权限或允许选择门店。

权限回归至少包含:

账号页面查询导出成本/毛利可选站点
普通无权限员工不可见拒绝拒绝不可见当前站点
仅查询员工可见成功拒绝按成本权限当前站点
查询+导出员工可见成功成功按成本权限当前站点
总部账号可见成功成功按总部规则必须显式选站

35. 离线延迟判断

flowchart TD
    A[业务库已有数据] --> B{报表源是什么}
    B -->|本地业务库| C[立即检查状态/分表/事务]
    B -->|DWD/Hologres| D[查询 DWD 是否存在]
    B -->|Report Service| E[直接请求服务核对]
    B -->|DataService| F[核对 Provider 响应]
    D --> G{DWD 有记录吗}
    G -->|没有| H[检查 ETL、水位、失败任务和 SLA]
    G -->|有| I[检查 where 和展示计算]
    E --> I
    F --> I

待外部系统确认的信息:

  • 各 DWD 表刷新方式与延迟 SLA。
  • 是否支持按单号触发重刷。
  • Report Service 的缓存和更新时间。
  • DataService 利润源表及财务关账后的重算规则。

36. 页面与导出不一致排查

第一步:保存页面请求的完整参数。
第二步:确认导出请求是否携带同样参数,尤其空值、数组和默认日期。
第三步:在 Service 层打印或复现最终 where/SQL。
第四步:分别取页面主键集合和导出主键集合做差集。
第五步:检查全量分页 offset、size、totalPage。
第六步:检查导出模板是否再次过滤、取绝对值、四舍五入或改符号。
第七步:冻结测试数据后重复一次,排除查询期间数据变化。

主键差集优先于金额差:先知道“多了哪几行”,金额问题通常会随之明确。

37. 金额不一致排查

按以下顺序拆解:

顺序核对内容
1数量:原始、出库、入库、退货、关闭
2单价:销售价、采购价、成本价
3折扣:整单还是明细,是否按数量分摊
4税:含税还是未税,当前报表是否根本未使用税字段
5状态:取消、关闭、草稿、审核中
6退款/撤销:是否反向冲回
7精度:BCMath 精度、行级四舍五入、合计后四舍五入

金额修复前必须保留原始字段快照和手工公式。

38. 空报表排查

1. 权限是否通过,接口是否实际成功而不是页面吞错。
2. sid 是否正确,总部账号是否漏传 storeId。
3. beginDate/endDate 是否按报表要求格式化。
4. where 使用 dw_billdate、billDate 还是 createTime。
5. 状态是否被排除。
6. 商品数据过滤是否把目标 SKU 排除。
7. 转站历史是否落在 180 天窗口外。
8. DWD/Report Service 是否已有该单。
9. 外部服务是否超时或返回非成功 code。

39. 常用代码检索命令

# 找报表入口与权限
rg -n "public function|checkpurview" application/controllers/reports

# 找报表物理表、日期与状态过滤
rg -n "dwd_|dw_billdate|bill_status|res_amount" application/Services/Report application/Providers

# 找金额和利润公式
rg -n "dis_amount|deduction|cost_price|gross|coreProfit|profitRate" application/controllers/reports application/Services/Report

# 找应收应付事件与期初欠款
rg -n "debt_amount|ContactDebt|billDate|createTime|balance" application/Services/Report/AccountReceiveSer.php application/Services/Report/AccountPaySer.php

# 找对账单状态、表和任务
rg -n "STATEMENT_BILL_|SEND_STATUS_|T_BS_STATEMENT|statement_id" application/KzData/Enums/StatementEnums.php application/Services/Statement application/models/statement application/config/tables.php

40. 查询模板

以下 SQL 是核对思路,表名、字段和分片必须按当前环境确认;默认只读,不直接更新报表层。

-- 1. DWD 是否有指定采购单
select bill_code, dw_billdate, qty, inqty, closeqty, price, amount, deduction
from dwd_bbyh_dgj_scm_po_order
where station_code = :sid and bill_code = :bill_code;

-- 2. 销售对账核心字段
select bill_code, dw_billdate, bill_status, amount, res_amount, create_time
from dwd_bbyh_dgj_sa_invoice_payment_info
where station_code = :sid and bill_code = :bill_code;

-- 3. 对账单主表按 sid 分片定位,实际后缀为 sid % 64
select id, contact_id, start_time, end_time, status, receive_status
from t_bs_statement_bill_v2_{shard}
where sid = :sid and contact_id = :contact_id
order by id desc;

不要把内部 SQL 参数直接拼到公开文档或日志中;生产查询使用只读账号和参数化工具。

41. 已确认风险清单

等级风险建议
P0库存流水固定站点条件入口启用前修复并做跨站隔离测试
P0库存流水调用未定义/错属方法增加专用处理方法和自动化测试
P1应收明细与汇总对历史欠款处理不同用迁移客户、普通客户分别核对
P1负毛利率部分实现可能归零明确产品口径并统一页面/汇总/导出
P1采购抵扣分摊可能除零增加 qty=0 防御和异常数据告警
P1往来账固定查询条数可能截断大客户长区间做超 500 条验证
P1利润查询方法显式权限不完整增加接口直连鉴权测试
P2手工 SQL 拼接模糊搜索统一转义或参数化,验证 %、引号等输入
P2DWD 与业务库同步 SLA 未在仓库定义补报表中心运维契约

42. 改动风险

报表改动的影响面通常大于代码行数:

  1. 改过滤条件会影响历史所有期间。
  2. 改公式会影响页面、合计、导出和财务解释。
  3. 改状态映射会影响旧数据兼容。
  4. 改 DWD 字段需要同步协调生产者、ETL 和消费者。
  5. 改分片规则会导致历史账单不可见。
  6. 改精度和四舍五入可能产生累计差异。
  7. 改对账单快照后不能静默覆盖已核对或已发送账单。

43. 回归数据集

至少准备以下业务样本:

样本必须验证
正常采购全量入库采购量=入库量,金额与抵扣分摊
部分入库后关闭inqty、closeqty、待入数量
采购退货审核中状态 1/2 合并展示
转站前、转站后和跨转站采购老站/新站数据合并与 180 天边界
正常销售出库原价、优惠、应收、成本、毛利
负毛利销售行、合计、导出毛利率
已取消出库销售对账排除
部分收款与全部收款res_amount 状态
销售退货和收款撤销应收方向与运行余额
超 500 条往来事件客户是否截断
对账单生成后再收款快照与实时应收差异
两个不同站点库存数据隔离与汇总
总部选店利润storeId 与权限

44. 自动化断言建议

断言 1:页面主键集合是同条件全量结果的子集。
断言 2:导出主键集合等于全量查询主键集合。
断言 3:sum 数量等于全量明细数量之和。
断言 4:总毛利 = Σ折后应收 - Σ成本。
断言 5:总毛利率 = 总毛利 / 总应收,而非平均行毛利率。
断言 6:取消出库不进入销售对账。
断言 7:当前 sid 不能查到其他站点库存。
断言 8:期末应收满足期初 + 销售 - 退货 - 收款 ± 调整。
断言 9:对账单任务重试不重复生成同期间同客户账单。
断言 10:未授权账号不能直连导出和利润查询接口。

45. 发布前回归清单

  • [ ] 页面查询与导出使用相同日期、站点和筛选项。
  • [ ] 当前页、全量汇总、导出三组数字分别核对。
  • [ ] 正常、取消、关闭、退货、部分完成状态均有样本。
  • [ ] 两个不同站点验证数据隔离。
  • [ ] 总部账号验证未选店和已选店。
  • [ ] 采购转站前后历史均验证。
  • [ ] 毛利、负毛利、零应收均验证。
  • [ ] 应收应付超过单次查询上限的数据集已验证。
  • [ ] 对账单草稿、核对、发送、失败重试均验证。
  • [ ] DWD/报表服务延迟已记录,而不是口头判断。
  • [ ] 已核对或已发送账单不会被静默覆盖。
  • [ ] SQL、日志和导出不泄露敏感客户数据。

46. 证据文件

结论代码证据
Hologres URL、批次和响应结构application/Services/Report/ReportBaseSer.php
采购 DWD、转站历史、采购公式application/Services/Report/PurchaseReportSer.php
采购入口和权限application/controllers/reports/PurchaseReport.php
销售筛选、对账与导出application/controllers/reports/SaleReport.php
销售 Hologres 兼容实现application/Services/Report/SaleReportSer.php
销售报表服务契约application/Providers/ReportService/SaleProvider.php
库存流水风险application/Services/Report/InventoryReportSer.php
利润公式application/Services/Report/ProfitReportSer.php
利润入口和总部选店application/controllers/reports/ProfitReport.php
应收事件和历史欠款application/Services/Report/AccountReceiveSer.php
应付事件application/Services/Report/AccountPaySer.php
对账单枚举application/KzData/Enums/StatementEnums.php
对账单生成与发送application/Services/Statement/*
对账单物理表application/config/tables.php、application/models/statement/*

47. 待外部系统确认

以下信息不能仅凭 DGJ2 仓库得出,确认后应回填本文:

  1. 每张 DWD 表的生产刷新频率、最大延迟和重跑方式。
  2. REPORT_SERVICE 各接口实际状态过滤与数据源表。
  3. DataService 利润数据的财务关账、重算和成本来源规则。
  4. 库存 DWD 中 rk_qty、ck_qty、qm_qty、qm 的精确定义。
  5. 应收应付 Model 固定 limit 是否内部自动翻页。
  6. 对账单策略在“零欠款是否发送”配置下的生产规则。
  7. 已核对、已发送对账单允许重生成的权限与审计要求。

48. 一句话记忆

先确认“哪个业务事实、按哪个日期、过滤哪些状态、来自哪个数据源”,再谈报表数字;先比主键集合,再比数量,最后比金额和格式。

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

多入口请求链路

场景调用方与入口请求载荷/上下文Controller/ConsumerService/Provider汇合点最终业务事实
采购/销售报表reports 页面/APIsid、组织、业务日期、状态、分页PurchaseReport.php/SaleReport.php对应 ReportSer/Provider单据主键 + 数据日期DWD/Hologres 聚合业务单据
库存/利润reports 页面/API仓库、SKU、期初期末、成本口径Inventory/Profit ControllerReport Service/DataServiceSKU/库存单/成本日期期初+变动=期末,销售-成本=利润
应收应付/对账财务报表客户/供应商、账期、状态Report ControllerAccountReceive/Pay/Statement Service来源单/对账单展示资金余额和核销过程
导出报表导出接口与列表相同筛选、列配置reports ControllerReportSer + ExportExcelrequest/export ID只读生成完整结果文件

日志证据矩阵

| 链路段 | 日志来源 | 可检索锚点 | 成功信号 | 失败信号 | 与下一段关联方式 | | --- | --- | --- | --- | --- | --- | --- | | 请求 | reports Controller | request_id、报表名、sid、日期/状态 | 校验通过,数据源查询发起 | 权限/日期/分页参数失败 | 查询 ID 与 Provider 关联 | | 数据源 | ReportSer/Provider/Hologres | query ID、SQL 模板、数据日期、耗时 | 返回行数和最大数据时间 | timeout、延迟、扫描过大 | 主键集合回到 OLTP 抽样 | | 口径转换 | ReportSer | 状态集合、日期字段、金额单位 | 聚合前后总额可解释 | 重复 join、漏状态、单位错误 | 单据主键集合与分组明细 | | 导出 | Export/Report | export ID、条件摘要、行数 | 列表/导出同条件总数一致 | 导出截断、列错位 | export ID + 抽样业务单号 |

环节数据变更台账

步骤代码位置事务读取事实写入表/缓存/MQ字段或数量变化回查证据
业务落库采购/销售/库存/财务 ServiceOLTP 事务业务动作OLTP 主明细/流水原始状态、数量、金额 old -> new业务单号、更新时间
数据同步MQ/ETL/报表数据源异步边界已提交 OLTP 增量DWD/Hologres源事实 -> 报表明细;存在同步延迟源更新时间、数据源最大时间
查询过滤ReportSer只读DWD 行、权限、筛选条件无过滤状态/日期/组织,不改原数据SQL 条件、主键集合
聚合计算Report Service只读过滤后的明细无sum/count/group;金额单位转换明确明细合计与汇总值
导出呈现ExportExcel只读报表结果文件DB 不变;格式化不改变数值语义列表/导出行数金额对比

子模块追踪:purchase-report 采购订单、退货与入库报表

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
查询口径采购报表页面/导出sid、日期、状态、采购/退货/入库单号application/controllers/reports/PurchaseReport.php -> application/Services/Report/PurchaseReportSer.php订单、退货、入库 DWD/OLTP 行,业务日期、软删和状态集合查询只读 不写;订单量、已入、已退、未履约按同一主键聚合request/query ID + filters + row count + max data time空报表先查权限、日期字段与离线水位;不以当前主状态替代过程事实
抽样对账报表金额/数量异常采购 billNo、entryId、报表行主键application/Providers/DataService/ReportProvider.php报表明细、采购主明细、入退库流水和税额单位查询只读 不写;明细合计应等于汇总,负向退货单独解释query ID + billNo/entryId + source timestampsOLTP 正确仅补 ETL/报表;源数据错误回领域流程修复,不直接改报表

子模块追踪:sale-report 销售明细、聚合与对账报表

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
查询聚合销售明细/汇总/导出sid、客户、日期、状态、销售单application/controllers/reports/SaleReport.php -> application/Services/Report/SaleReportSer.php销售、有效出库、销退、收款和软删行查询只读 不写;销售正向、出库/退货按口径换符号后 sum/grouprequest/query ID + sale billNo + raw/final rows列表和汇总差异先比过滤、join 粒度和分页截断
对账抽样销售金额/毛利/核销不平销售/出库/收款单、entryIdapplication/Providers/ReportService/SaleProvider.php含税/未税额、成本快照、退款与核销事实查询只读 不写;同一明细金额公式在页面与导出一致query ID + related billNos + amount formula负毛利可能为历史成本事实;修复前先证明公式还是源快照错误

子模块追踪:inventory-report 库存流水报表

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
流水查询库存流水页面/导出sid、SKU、仓库货位、日期、transTypeapplication/controllers/reports/InventoryReport.php -> application/Services/Report/InventoryReportSer.php分片流水、业务单、交易类型、软删和归档截面查询只读 不写;入库正数、出库负数,分片和月份统一后求净额request/query ID + SKU + transType + period170502 等冲突码必须联合 billType;不能只按 transType 文案解释
实时对账流水期末与实时库存不符四维库存键、起止截面application/Services/Report/InventoryReportSer.php期初归档、期间有效流水、实时四维量查询只读 不写;验证 期末=期初+期间净额query ID + inventory key + source max time报表延迟与库存脏数据分开;主库存正确只补离线同步

子模块追踪:profit-report 利润表与成本口径

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
利润查询利润表/明细/导出sid、日期、客户、商品、组织application/controllers/reports/ProfitReport.php -> application/Services/Report/ProfitReportSer.php销售收入、退货、成本快照、税费和权限范围查询只读 不写;利润按已确认公式 收入-成本-相关调整 聚合request/query ID + dimensions + amount components权限或日期口径导致空值时零写入;不拿当前采购价改历史成本
公式复核毛利异常或明细汇总不等销售/出库/退货主键application/Services/Report/ProfitReportSer.php每行收入、成本、数量符号和 join 倍增查询只读 不写;逐行重算并与 group sum 比较query ID + business keys + formula inputs重复 join 先修查询;源成本缺失回数据同步负责人补事实

子模块追踪:receivable-report 应收账款报表

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
应收查询客户应收列表/账龄/导出sid、客户、截止日、状态application/controllers/reports/CustomerReport.php -> application/Services/Report/AccountReceiveSer.php期初欠款、期间销售/收款/退款/核销和有效状态查询只读 不写;期末=期初+新增应收-收款-退款/调整request/query ID + customer/sid + cutoff截止日与业务日统一;明细/汇总差异先查期初和过滤范围
资金抽样已收款仍显示应收销售/收款单、客户 IDapplication/Services/Report/CustomerReportSer.php有效收款、核销关系、软删和离线水位查询只读 不写;原单与资金单按稳定键关联query ID + sale/receipt billNo + max timeOLTP 核销正确只补同步;未核销走财务领域处理

子模块追踪:payable-report 应付账款报表

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
应付查询供应商应付/账龄/导出sid、供应商、业务日/入账日、截止日application/Services/Report/AccountPaySer.php期初、采购入库/退货、付款退款和双时间口径查询只读 不写;按选择日期口径计算期间增减与余额request/query ID + supplier/sid + date type业务日与入账日不能混查;分页截断时核对总数和游标
支付抽样已付款仍有应付或金额不平采购/支付/退款单application/Providers/DataService/ReportProvider.phpPayment/Info、采购支付关系和报表事实查询只读 不写;统一分/元单位后对账query ID + po/pay/refund billNos支付主账正确只补离线层;金额单位错误先修转换公式

子模块追踪:statement-report 对账单生成与发送

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
生成对账用户/任务按周期生成statement ID、sid、客户/供应商、期间application/Services/Report/ReportBaseSer.php截止水位、业务类型、明细集合和已有任务任务本地事务 none/pending -> processing;异步数据源生成明细与汇总statement/batch ID + period + item count同一范围幂等;部分生成保留失败分片,不重复成功分片
发送回查对账单完成后导出/发送statement ID、recipient、file/task IDapplication/KzData/Enums/StatementEnums.php状态、汇总金额、文件完成与发送记录状态事务 processing -> success/partial/failed/sent;通知在事务外statement/file/send ID + row/amount文件成功不等于通知送达;只补失败文件或发送步骤

子模块追踪:report-export-delay 报表导出、权限与离线延迟

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
导出权限报表页面查询或导出按钮userId、sid、report/export code、filtersapplication/Services/Report/ReportBaseSer.php查询/导出独立权限、组织范围、筛选和数据水位查询只读;导出任务 pending -> processing,业务 DB 不写request/export ID + permission + filters页面有权不代表导出有权;403 零任务或业务写入
延迟判断页面/导出/OLTP 结果不一致query/export ID、业务单、三个最大时间application/Providers/DataService/ReportProvider.phpOLTP updatedAt、DWD/Hologres max time、文件生成水位查询只读 不写;按时间差判断同步中或口径错误query/export ID + source max timestamps等待超过 SLA 后只补 ETL/导出任务;SLA 需目标环境确认