本文回答的不是“报表代码放在哪里”,而是“某个数字为什么是这个值、它何时进入报表、页面与导出为什么可能不同、应当沿哪条链路核对”。
已直接核验 DGJ2 当前代码;报表中心内部 ETL、DataService 实际物理表和生产同步 SLA 不在本仓库中,统一标为 待外部系统确认。
1. 业务目标
DGJ2 报表横跨业务库、分表、DWD/Hologres、独立报表服务和数据服务。一个“金额不一致”可能来自五类差异:
- 业务含义不同:订单金额、出库金额、应收金额、实收金额不是同一个指标。
- 时间口径不同:创建时间、业务日期、出库日期、入库日期、月份不同。
- 状态口径不同:草稿、关闭、取消、退货、部分收款是否纳入不同。
- 数据源时效不同:业务库已更新,离线 DWD 尚未同步。
- 展示层计算不同:列表、汇总行、导出模板可能再次计算或格式化。
本文把采购、销售、库存、利润、应收、应付和对账单放在同一套核对框架下。
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. 入口文件地图
| 业务 | Controller | Service / Provider |
|---|---|---|
| 采购报表 | application/controllers/reports/PurchaseReport.php | application/Services/Report/PurchaseReportSer.php |
| 销售报表 | application/controllers/reports/SaleReport.php | application/Services/Report/SaleReportSer.php、application/Providers/ReportService/SaleProvider.php |
| 库存报表 | application/controllers/reports/InventoryReport.php | application/Services/Report/InventoryReportSer.php |
| 利润报表 | application/controllers/reports/ProfitReport.php | application/Services/Report/ProfitReportSer.php、application/Providers/DataService/ReportProvider.php |
| 客户报表 | application/controllers/reports/CustomerReport.php | application/Services/Report/CustomerReportSer.php |
| 应收 | 由对应报表 Controller 调用 | application/Services/Report/AccountReceiveSer.php |
| 应付 | 由对应报表 Controller 调用 | application/Services/Report/AccountPaySer.php |
| 对账单 | StatementBill.php、StatementSend.php、StatementStrategy.php | application/Services/Statement/* |
| 公共 Hologres | - | application/Services/Report/ReportBaseSer.php |
6. 数据源与物理表字典
| 常量或逻辑名 | 物理表 / 服务 | 主要用途 |
|---|---|---|
PODETAILTABLE | dwd_bbyh_dgj_scm_po_order | 采购订单明细 |
PURETURNTABLE | dwd_bbyh_dgj_scm_po_order_return | 采购退货明细 |
PUDETAILTABLE | dwd_bbyh_dgj_scm_pu_invoice | 采购入库明细 |
ARREARSTABLE | dwd_dgj_home_arrears | 首页应收应付余额 |
PUOUTTABLE | dwd_dgj_out_bound | 出库与微仓补货统计 |
ACTIVITY_GIFT_TABLE | dwd_dgj_activity_gift | 赠品活动订单 |
ACTIVITY_NEW_SID_TABLE | dwd_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[数字格式化与字段重命名]
页面与导出一致,至少需要同时满足:
- 复用同一套筛选构造方法。
- 导出循环分页没有漏页或重复页。
- 页面汇总不是只汇总当前页。
- 模板没有再次改变符号或公式。
- 查询期间数据没有继续变化。
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_QUERY | PUREOORTDETAIL_EXPORT |
puOrderDetailGoodsReport | 按商品汇总入库 | PUREPORTINV_QUERY | PUREPORTINV_EXPORT |
puOrderDetailSupplyReport | 按供应商汇总入库 | PUREPORTPUR_QUERY | PUREPORTPUR_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_id | SKU |
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)
毛利合计 = 折后应收合计 - 成本金额合计
总毛利率必须用“总毛利 ÷ 总应收”,不能平均每行毛利率。
示例:
| 行 | 应收 | 成本 | 毛利率 |
|---|---|---|---|
| A | 100 | 50 | 50% |
| B | 900 | 810 | 10% |
| 正确合计 | 1000 | 860 | 14% |
简单平均得到 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 与筛选条件,但导出还会:
- 循环全部分页。
- 将金额执行
number_format。 - 映射展示名称。
- 在模板中生成合计行。
因此核对顺序应为:
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 常量 | 金额字段 |
|---|---|---|
| 主营业务收入与成本 | coreBiz | sale_fee、cost_fee |
| 其他业务收入 | otherIncome | qtsr_fee |
| 其他业务成本 | otherCost | qtzc_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 重复,因此没有再次加到期初。
这意味着:
- 两种方法对历史欠款的语义不同。
- 数据迁移方式不同的客户可能出现明细与汇总不一致。
- 修复前要识别
debt_amount是余额快照还是导入事件累计。
不能简单取消注释或双边都相加,否则可能重复计入。
28. 应付账款口径
应付同样采用事件流:
期初应付
= 期初前采购入库
- 期初前付款
+ 供应商扩展表 debt_amount
期间运行余额:
运行余额 += 采购入库金额 - 采购退货金额 - 付款金额 + 付款撤销影响
代码把采购类事件与付款事件合并后按 createTime 排序,再逐笔计算余额。
28.1 时间双口径
往来账展示使用 billDate,事件排序使用 createTime。因此同一业务日内后补录或回调延迟的记录,展示日期相同但运行余额顺序可能不同。
出现“日末余额正确、日内顺序看起来不对”时,应比较两个字段。
29. 往来账分页与截断风险
部分底层查询调用使用固定 limit=500、offset=1 形态。是否由 Model 内部继续遍历,需要结合实际方法确认。
高频客户或供应商在大区间内超过 500 条时,必须验证:
业务库事件总数 == Service 合并前事件数 == 页面/导出明细数
如果不等,先排查分页截断,不能用“余额碰巧相等”证明完整。
30. 对账单不是实时查询
对账单将指定期间的业务事实生成并保存为独立快照:
| 表常量 | 物理表 | 作用 |
|---|---|---|
T_BS_STATEMENT_BILL | t_bs_statement_bill_v2,按 64 分片 | 对账单主表 |
T_BS_STATEMENT_BILL_DETAIL | t_bs_statement_bill_detail_v2 | 业务明细 |
T_BS_STATEMENT_BILL_DETAIL_ITEM | t_bs_statement_bill_detail_item_v2 | 商品物料明细 |
T_BS_STATEMENT_BILL_TASK | t_bs_statement_bill_task_v2 | 自动生成任务 |
T_BS_NEED_STATEMENT_USER | t_bs_need_statement_user | 需要对账的客户 |
T_BS_STATEMENT_STRATEGY | t_bs_statement_strategy_v2 | 日/月策略 |
T_BS_STATEMENT_STRATEGY_RELATION | t_bs_statement_strategy_relation_v2 | 策略与客户关系 |
T_BS_STATEMENT_SEND_RECORD | t_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. 报表权限核对
报表通常存在三种权限:
- 页面菜单权限。
- 查询接口权限。
- 导出和成本字段权限。
销售报表还可能单独控制成本价和毛利字段。总部账号可能跳过站点权限或允许选择门店。
权限回归至少包含:
| 账号 | 页面 | 查询 | 导出 | 成本/毛利 | 可选站点 |
|---|---|---|---|---|---|
| 普通无权限员工 | 不可见 | 拒绝 | 拒绝 | 不可见 | 当前站点 |
| 仅查询员工 | 可见 | 成功 | 拒绝 | 按成本权限 | 当前站点 |
| 查询+导出员工 | 可见 | 成功 | 成功 | 按成本权限 | 当前站点 |
| 总部账号 | 可见 | 成功 | 成功 | 按总部规则 | 必须显式选站 |
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 拼接模糊搜索 | 统一转义或参数化,验证 %、引号等输入 |
| P2 | DWD 与业务库同步 SLA 未在仓库定义 | 补报表中心运维契约 |
42. 改动风险
报表改动的影响面通常大于代码行数:
- 改过滤条件会影响历史所有期间。
- 改公式会影响页面、合计、导出和财务解释。
- 改状态映射会影响旧数据兼容。
- 改 DWD 字段需要同步协调生产者、ETL 和消费者。
- 改分片规则会导致历史账单不可见。
- 改精度和四舍五入可能产生累计差异。
- 改对账单快照后不能静默覆盖已核对或已发送账单。
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 仓库得出,确认后应回填本文:
- 每张 DWD 表的生产刷新频率、最大延迟和重跑方式。
REPORT_SERVICE各接口实际状态过滤与数据源表。- DataService 利润数据的财务关账、重算和成本来源规则。
- 库存 DWD 中
rk_qty、ck_qty、qm_qty、qm的精确定义。 - 应收应付 Model 固定 limit 是否内部自动翻页。
- 对账单策略在“零欠款是否发送”配置下的生产规则。
- 已核对、已发送对账单允许重生成的权限与审计要求。
48. 一句话记忆
先确认“哪个业务事实、按哪个日期、过滤哪些状态、来自哪个数据源”,再谈报表数字;先比主键集合,再比数量,最后比金额和格式。
请求-日志-数据变更追踪卡
多入口请求链路
| 场景 | 调用方与入口 | 请求载荷/上下文 | Controller/Consumer | Service/Provider | 汇合点 | 最终业务事实 |
|---|---|---|---|---|---|---|
| 采购/销售报表 | reports 页面/API | sid、组织、业务日期、状态、分页 | PurchaseReport.php/SaleReport.php | 对应 ReportSer/Provider | 单据主键 + 数据日期 | DWD/Hologres 聚合业务单据 |
| 库存/利润 | reports 页面/API | 仓库、SKU、期初期末、成本口径 | Inventory/Profit Controller | Report Service/DataService | SKU/库存单/成本日期 | 期初+变动=期末,销售-成本=利润 |
| 应收应付/对账 | 财务报表 | 客户/供应商、账期、状态 | Report Controller | AccountReceive/Pay/Statement Service | 来源单/对账单 | 展示资金余额和核销过程 |
| 导出 | 报表导出接口 | 与列表相同筛选、列配置 | reports Controller | ReportSer + ExportExcel | request/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 | 字段或数量变化 | 回查证据 |
|---|---|---|---|---|---|---|
| 业务落库 | 采购/销售/库存/财务 Service | OLTP 事务 | 业务动作 | 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 timestamps | OLTP 正确仅补 ETL/报表;源数据错误回领域流程修复,不直接改报表 |
子模块追踪:sale-report 销售明细、聚合与对账报表
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 查询聚合 | 销售明细/汇总/导出 | sid、客户、日期、状态、销售单 | application/controllers/reports/SaleReport.php -> application/Services/Report/SaleReportSer.php | 销售、有效出库、销退、收款和软删行 | 查询只读 不写;销售正向、出库/退货按口径换符号后 sum/group | request/query ID + sale billNo + raw/final rows | 列表和汇总差异先比过滤、join 粒度和分页截断 |
| 对账抽样 | 销售金额/毛利/核销不平 | 销售/出库/收款单、entryId | application/Providers/ReportService/SaleProvider.php | 含税/未税额、成本快照、退款与核销事实 | 查询只读 不写;同一明细金额公式在页面与导出一致 | query ID + related billNos + amount formula | 负毛利可能为历史成本事实;修复前先证明公式还是源快照错误 |
子模块追踪:inventory-report 库存流水报表
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 流水查询 | 库存流水页面/导出 | sid、SKU、仓库货位、日期、transType | application/controllers/reports/InventoryReport.php -> application/Services/Report/InventoryReportSer.php | 分片流水、业务单、交易类型、软删和归档截面 | 查询只读 不写;入库正数、出库负数,分片和月份统一后求净额 | request/query ID + SKU + transType + period | 170502 等冲突码必须联合 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 | 截止日与业务日统一;明细/汇总差异先查期初和过滤范围 |
| 资金抽样 | 已收款仍显示应收 | 销售/收款单、客户 ID | application/Services/Report/CustomerReportSer.php | 有效收款、核销关系、软删和离线水位 | 查询只读 不写;原单与资金单按稳定键关联 | query ID + sale/receipt billNo + max time | OLTP 核销正确只补同步;未核销走财务领域处理 |
子模块追踪:payable-report 应付账款报表
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 应付查询 | 供应商应付/账龄/导出 | sid、供应商、业务日/入账日、截止日 | application/Services/Report/AccountPaySer.php | 期初、采购入库/退货、付款退款和双时间口径 | 查询只读 不写;按选择日期口径计算期间增减与余额 | request/query ID + supplier/sid + date type | 业务日与入账日不能混查;分页截断时核对总数和游标 |
| 支付抽样 | 已付款仍有应付或金额不平 | 采购/支付/退款单 | application/Providers/DataService/ReportProvider.php | Payment/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 ID | application/KzData/Enums/StatementEnums.php | 状态、汇总金额、文件完成与发送记录 | 状态事务 processing -> success/partial/failed/sent;通知在事务外 | statement/file/send ID + row/amount | 文件成功不等于通知送达;只补失败文件或发送步骤 |
子模块追踪:report-export-delay 报表导出、权限与离线延迟
| 环节 | 入口/触发 | 请求/业务键 | 代码链路 | 读取事实 | 写入与字段变化 | 日志证据 | 异常与补偿 |
|---|---|---|---|---|---|---|---|
| 导出权限 | 报表页面查询或导出按钮 | userId、sid、report/export code、filters | application/Services/Report/ReportBaseSer.php | 查询/导出独立权限、组织范围、筛选和数据水位 | 查询只读;导出任务 pending -> processing,业务 DB 不写 | request/export ID + permission + filters | 页面有权不代表导出有权;403 零任务或业务写入 |
| 延迟判断 | 页面/导出/OLTP 结果不一致 | query/export ID、业务单、三个最大时间 | application/Providers/DataService/ReportProvider.php | OLTP updatedAt、DWD/Hologres max time、文件生成水位 | 查询只读 不写;按时间差判断同步中或口径错误 | query/export ID + source max timestamps | 等待超过 SLA 后只补 ETL/导出任务;SLA 需目标环境确认 |