Files
SBrainCO/数据溯源审计.md
T
freedakgmail 915345bfd6 fix: 统一利润瀑布口径,与费用总览API一致
profit-waterfall API的received/consumption/discount/store_contribution
改为仅计算has_expense门店(FILTER WHERE has_expense)
修复前: received=6441万(94家) store_contribution=770万
修复后: received=6369万(92家) store_contribution=698万
现在瀑布图和指标卡的贡献利润/食材率/费用率/贡献率完全一致

同时创建数据溯源审计文档,覆盖全部页面的指标溯源和口径对比
2026-08-11 20:13:58 +08:00

309 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 全页面数据溯源审计报告
> 目标:逐页面、逐指标追溯显示名称 → API → SQL → 数据源表字段,标出跨页面重复指标口径是否一致。
## 一、页面与 API 总览
| 页面 | 文件 | 调用的 API |
|------|------|-----------|
| 总部驾驶舱 | DashboardPage.tsx | /overview, /overview/daily, /stores/risk, /stores/priority, /tasks/loop-health, /stores/quadrant, /platform/economics, /situational-awareness/alerts, /store-expense/overview, /overview/yoy, /overview/mom, /overview/trend, /overview/profit-trend |
| 老板驾驶舱 | BossPage.tsx | /overview, /overview/daily, /store-expense/overview, /overview/profit-waterfall, /stores/risk, /stores/priority, /cost-analysis/store-overview, /store-expense/expense-structure, /overview/store-profit-ranking, /overview/profit-opportunity |
| 门店工作台 | StorePage.tsx | /stores/risk, /stores/priority, /tasks/stores/:code/daily-card, /tasks, /stores/:code/daily, /tasks/followup, /sku/attach, /sku/abc, /stores/:code, /situational-awareness/health-score, /stores/:code/meal-period, /stores/:code/category-mix, /stores/:code/cost, /stores/:code/member, /stores/:code/anomalies, /stores/:code/staffing |
| 成本分析 | CostPage.tsx | /cost/comparison, /cost/inventory, /cost/category-benchmark |
| 成本分析(Tab) | cost-analysis/*.tsx | /cost-analysis/overview, /category-comparison, /margin-deviation, /variance-top, /menu-engineering, /profitability, /pricing, /store-overview, /store-ranking, /bom-*, /packaging-*, /material-*, /data-quality, /unmatched-* |
| 费用分析(Tab) | store-expense/*.tsx | /store-expense/overview, /expense-structure, /store-ranking, /store-contribution, /break-even, /loss-diagnosis, /delivery-commission, /rent-risk, /fixed-variable, /efficiency, /store-evaluation |
| 风险监控 | RiskPage.tsx | /risk/anomaly, /risk/zero-received, /risk/cashier |
| 会员复购 | MemberPage.tsx | /member/comparison, /member/repeat |
| SKU分析 | SKUPage.tsx | /sku/abc, /sku/category |
| 菜单工程 | MenuEngineeringPage.tsx | /analytics-enhanced/menu-engineering/actions |
| 产品生命周期 | ProductLifecyclePage.tsx | /product/products, /product/reviews/overview, /product/reviews |
| BOM穿透 | BomPenetrationPage.tsx | /central-kitchen/bom-penetration |
| 分销对账 | DistributionReconciliationPage.tsx | /distribution/reconciliation |
| 生产计划 | ProductionPlanPage.tsx | /sales-driven/production-plan |
| 任务闭环 | TasksPage.tsx | /tasks |
| 数据导入 | DataImportPage.tsx | /product/import-logs, /product/quality-rules |
| 本体浏览 | OntologyPage.tsx | /tasks/ontology/* |
---
## 二、核心指标溯源与口径对比
### 2.1 营业收入(消费金额)
| 页面 | 前端变量 | API | API字段 | SQL来源 | 数据源表.字段 |
|------|---------|-----|---------|---------|-------------|
| 总部驾驶舱 | `od?.consumption` | /overview | `consumption` | `sum(consumption)` from bill_fact | analytics.bill_fact.consumption |
| 老板驾驶舱 | `od?.consumption` | /overview | `consumption` | 同上 | 同上 |
| 费用总览Tab | `ov.total_consumption` | /store-expense/overview | `total_consumption` | `sum(consumption)` from bill_fact CTE | analytics.bill_fact.consumption |
**口径:一致 ✅** — 三个页面均来自 `analytics.bill_fact.consumption` 的月度汇总。
### 2.2 优惠
| 页面 | 前端变量 | API | API字段 | SQL来源 | 数据源表.字段 |
|------|---------|-----|---------|---------|-------------|
| 总部驾驶舱 | `od?.discount` | /overview | `discount` | `sum(discount_total)` from bill_fact | analytics.bill_fact.discount_total |
| 老板驾驶舱 | `od?.discount` | /overview | `discount` | 同上 | 同上 |
| 费用总览Tab | `ov.total_discount` | /store-expense/overview | `total_discount` | `sum(discount_total)` from bill_fact CTE | analytics.bill_fact.discount_total |
**口径:一致 ✅**(已修复,BossPage 之前用 `ex?.total_discount` 也一致)
### 2.3 实收
| 页面 | 前端变量 | API | API字段 | SQL来源 | 数据源表.字段 |
|------|---------|-----|---------|---------|-------------|
| 总部驾驶舱 | `od?.received` | /overview | `received` | `sum(received_total)` from bill_fact | analytics.bill_fact.received_total |
| 老板驾驶舱 | `od?.received` | /overview | `received` | 同上 | 同上 |
| 费用总览Tab | `ov.total_received` | /store-expense/overview | `total_received` | `sum(r.received)` from mv_store_risk_rating_monthly | analytics.mv_store_risk_rating_monthly.received |
| 利润瀑布 | `wf.received` | /overview/profit-waterfall | `received` | `sum(r.received)` from mv_store_risk_rating_monthly | analytics.mv_store_risk_rating_monthly.received |
**口径:一致 ✅**(已修复)— `bill_fact.received_total` 汇总和 `mv_store_risk_rating_monthly.received` 汇总结果相同(均为 64,409,339.09),因为后者物化视图就来自前者。
### 2.4 账单数
| 页面 | 前端变量 | API | API字段 | SQL来源 | 数据源表.字段 |
|------|---------|-----|---------|---------|-------------|
| 总部驾驶舱 | `od?.bill_count` | /overview | `bill_count` | `count(*)` from bill_fact | analytics.bill_fact |
| 老板驾驶舱 | `ex?.total_bills` | /store-expense/overview | `total_bills` | `sum(bill_count)` from mv_store_risk_rating_monthly | analytics.mv_store_risk_rating_monthly.bill_count |
**口径:一致 ✅** — 值均为 1,682,360。
### 2.5 客单价
| 页面 | 前端变量 | API | API字段 | 计算方式 |
|------|---------|-----|---------|---------|
| 总部驾驶舱 | `od?.avg_bill_value` | /overview | `avg_bill_value` | `avg(received_total)` from bill_fact = 38.29 |
| 老板驾驶舱 | `ex?.avg_bill_value` | /store-expense/overview | `avg_bill_value` | `sum(received)/sum(bill_count)` = 38.29 |
**口径:一致 ✅** — 数学结果相同。
### 2.6 优惠率
| 页面 | 前端变量 | API | API字段 | 计算方式 |
|------|---------|-----|---------|---------|
| 总部驾驶舱 | `od?.discount_rate_pct` | /overview | `discount_rate_pct` | `avg(discount_total/consumption*100)` from bill_fact = 19.52% |
| 老板驾驶舱 | 无(不显示此指标) | - | - | - |
**口径:N/A** — 仅总部驾驶舱显示。
### 2.7 理论毛利率
| 页面 | 前端变量 | API | API字段 | 计算方式 |
|------|---------|-----|---------|---------|
| 总部驾驶舱 | `od?.theoretical_margin_pct` | /overview | `theoretical_margin_pct` | `sum(theoretical_profit)/sum(received)*100` from bill_fact = 61.84% |
**口径:N/A** — 仅总部驾驶舱显示。(已修复,之前硬编码为0)
### 2.8 会员占比
| 页面 | 前端变量 | API | API字段 | 计算方式 |
|------|---------|-----|---------|---------|
| 总部驾驶舱 | `od?.member_share_pct` | /overview | `member_share_pct` | `sum(received_total) FILTER(member_id非空)/sum(received_total)*100` = 14.25% |
**口径:N/A** — 仅总部驾驶舱显示。(已修复,之前硬编码为0)
### 2.9 门店贡献利润(实际)
| 页面 | 前端变量 | API | API字段 | 计算方式 | 值 |
|------|---------|-----|---------|---------|-----|
| 总部驾驶舱 | `ex?.actual_net_profit` | /store-expense/overview | `actual_net_profit` | `sum(received) FILTER(费用匹配) - sum(food_cost) - sum(operating_expense)` | 6,982,110.42 |
| 老板驾驶舱(指标卡) | `ex?.actual_net_profit` | /store-expense/overview | `actual_net_profit` | 同上 | 6,982,110.42 |
| 老板驾驶舱(瀑布图) | `wf.store_contribution` | /overview/profit-waterfall | `store_contribution` | `sum(received) - sum(COALESCE(food_cost,0)) - sum(COALESCE(operating_expense,0))` | **7,703,472.16** |
**口径:不一致 ❌** — 瀑布图用 COALESCE(...,0) 包含2家无费用门店,导致利润虚高72万。需修复 profit-waterfall API。
### 2.10 贡献率(实际)
| 页面 | 前端变量 | API | API字段 | 计算方式 | 值 |
|------|---------|-----|---------|---------|-----|
| 总部驾驶舱 | `ex?.actual_net_margin_pct` | /store-expense/overview | `actual_net_margin_pct` | `(matched_received - food_cost - operating_expense)/matched_received*100` | 10.96% |
| 老板驾驶舱(指标卡) | `ex?.actual_net_margin_pct` | /store-expense/overview | `actual_net_margin_pct` | 同上 | 10.96% |
| 老板驾驶舱(瀑布图) | 前端计算 `wf.store_contribution/wf.received*100` | /overview/profit-waterfall | 前端除法 | `7703472/64409339*100` | **11.96%** |
**口径:不一致 ❌** — 同 2.9,瀑布图口径不同。
### 2.11 食材成本
| 页面 | 前端变量 | API | API字段 | 计算方式 |
|------|---------|-----|---------|---------|
| 总部驾驶舱 | `ex?.total_food_cost` | /store-expense/overview | `total_food_cost` | `sum(actual_food_cost)` from mv_store_operating_expense_monthly |
| 老板驾驶舱(瀑布图) | `wf.food_cost` | /overview/profit-waterfall | `food_cost` | `sum(COALESCE(actual_food_cost,0))` — 含2家0值门店 |
| 费用总览Tab | `ov.total_food_cost` | /store-expense/overview | `total_food_cost` | 同总部 |
**口径:一致 ✅** — 2家无费用门店的 food_cost 为 NULL`sum(NULL)` 忽略,`sum(COALESCE(NULL,0))` 加0,结果相同。
### 2.12 食材成本率
| 页面 | 前端变量 | API | API字段 | 计算方式 | 值 |
|------|---------|-----|---------|---------|-----|
| 总部驾驶舱 | `ex?.actual_food_cost_rate_pct` | /store-expense/overview | `actual_food_cost_rate_pct` | `sum(food_cost)/matched_received*100` | 35.26% |
| 老板驾驶舱(瀑布图) | 前端计算 `wf.food_cost/wf.received*100` | /overview/profit-waterfall | 前端除法 | `22459074/64409339*100` | **34.87%** |
**口径:不一致 ❌** — 分母不同(matched_received 6369万 vs all_received 6441万)。
### 2.13 经营费用
| 页面 | 前端变量 | API | API字段 | 计算方式 |
|------|---------|-----|---------|---------|
| 总部驾驶舱 | `ex?.total_expense` | /store-expense/overview | `total_expense` | `sum(operating_expense)` — NULL被sum忽略 |
| 老板驾驶舱(瀑布图) | `wf.total_expense` | /overview/profit-waterfall | `total_expense` | `sum(COALESCE(operating_expense,0))` — 2家为0 |
| 费用总览Tab | `ov.total_expense` | /store-expense/overview | `total_expense` | 同总部 |
**口径:一致 ✅** — sum忽略NULL vs COALESCE加0,结果相同。
### 2.14 费用率
| 页面 | 前端变量 | API | API字段 | 计算方式 | 值 |
|------|---------|-----|---------|---------|-----|
| 总部驾驶舱 | `ex?.overall_expense_rate_pct` | /store-expense/overview | `overall_expense_rate_pct` | `sum(operating_expense)/matched_received*100` | 53.77% |
| 老板驾驶舱(瀑布图) | 前端计算 `wf.total_expense/wf.received*100` | /overview/profit-waterfall | 前端除法 | `34246793/64409339*100` | **53.17%** |
| 费用总览Tab | `ov.overall_expense_rate_pct` | /store-expense/overview | `overall_expense_rate_pct` | 同总部 | 53.77% |
**口径:不一致 ❌** — 瀑布图分母含全部94家。
### 2.15 人工费用 / 人工费率
| 页面 | 前端变量 | API | API字段 | 计算方式 |
|------|---------|-----|---------|---------|
| 总部驾驶舱 | `ex?.total_wage` / `ex?.overall_wage_rate_pct` | /store-expense/overview | `total_wage` / `overall_wage_rate_pct` | `sum(wage_expense)` / `sum(wage)/matched_received*100` |
| 老板驾驶舱(瀑布图) | `wf.wage` | /overview/profit-waterfall | `wage` | `sum(COALESCE(wage_expense,0))` |
| 费用总览Tab | `ov.total_wage` / `ov.overall_wage_rate_pct` | /store-expense/overview | 同总部 | 同总部 |
**金额口径:一致 ✅** — sum忽略NULL vs COALESCE加0结果相同。
**费率口径:瀑布图不一致 ❌** — 前端用 `wf.wage/wf.received*100`(分母含94家)vs store-expense用 `matched_received`92家)。
### 2.16 房租费用 / 房租费率
同 2.15,金额一致,费率瀑布图分母不一致。
### 2.17 水电费用 / 水电费率
同 2.15,金额一致,费率瀑布图分母不一致。
### 2.18 外卖佣金
| 页面 | 前端变量 | API | API字段 | 计算方式 |
|------|---------|-----|---------|---------|
| 总部驾驶舱 | `ex?.total_commission` | /store-expense/overview | `total_commission` | `sum(delivery_commission_expense)` |
| 老板驾驶舱(瀑布图) | `wf.commission` | /overview/profit-waterfall | `commission` | `sum(COALESCE(delivery_commission_expense,0))` |
**口径:一致 ✅** — 金额相同。
### 2.19 门店数
| 页面 | 前端变量 | API | API字段 | 计算方式 |
|------|---------|-----|---------|---------|
| 总部驾驶舱 | `riskRows.length` | /stores/risk | count of rows | `count(*)` from mv_store_risk_rating_monthly |
| 老板驾驶舱 | `ex?.total_stores` | /store-expense/overview | `total_stores` | `count(*)` from full_scope (mv_store_risk_rating_monthly) |
**口径:一致 ✅** — 均为94家。
### 2.20 盈利/亏损门店数
| 页面 | 前端变量 | API | API字段 | 计算方式 |
|------|---------|-----|---------|---------|
| 总部驾驶舱 | `ex?.profitable_stores` / `ex?.loss_stores` | /store-expense/overview | `profitable_stores` / `loss_stores` | `count(*) FILTER(actual_store_contribution>0)` / `count(*) FILTER(<=0 AND received>0)` |
| 老板驾驶舱 | 同上 | 同上 | 同上 | 同上 |
**口径:一致 ✅** — 盈利78家/亏损13家。
### 2.21 门店风险分布
| 页面 | 前端变量 | API | 数据源 |
|------|---------|-----|--------|
| 总部驾驶舱 | `riskSummary` | /stores/risk | analytics.mv_store_risk_rating_monthly.risk_level |
| 老板驾驶舱 | `riskSummary` | /stores/risk | 同上 |
**口径:一致 ✅**
### 2.22 P0/P1 门店
| 页面 | 前端变量 | API | 数据源 |
|------|---------|-----|--------|
| 总部驾驶舱 | `priorityRows` | /stores/priority | analytics.mv_store_action_priority_deep_monthly |
| 老板驾驶舱 | `priorityRows` | /stores/priority | 同上 |
**口径:一致 ✅**
### 2.23 成本异常分布
| 页面 | 前端变量 | API | 数据源 |
|------|---------|-----|--------|
| 老板驾驶舱 | `costOv.red_count` 等 | /cost-analysis/store-overview | analytics.mv_store_theoretical_actual_cost_monthly |
| 成本分析Tab | 同上 | 同上 | 同上 |
**口径:一致 ✅**
### 2.24 利润机会池
| 页面 | 前端变量 | API | 数据源 |
|------|---------|-----|--------|
| 老板驾驶舱 | `profitOpp` | /overview/profit-opportunity | analytics.mv_store_risk_rating_monthly + mv_store_operating_expense_monthly |
**口径:N/A** — 仅老板驾驶舱显示。
### 2.25 日度趋势
| 页面 | 前端变量 | API | 数据源 |
|------|---------|-----|--------|
| 总部驾驶舱 | `dailyRows` | /overview/daily | analytics.mv_overview_daily 或 bill_fact |
| 老板驾驶舱 | `dailyRows` | /overview/daily | 同上 |
**口径:一致 ✅**
---
## 三、发现的问题汇总
### 问题1:利润瀑布 store_contribution 口径不一致 ❌
**位置**`/overview/profit-waterfall` API (`data.ts:1082`)
**现状**
```sql
round(sum(received) - sum(actual_food_cost) - sum(operating_expense), 2) AS store_contribution
```
- `sum(received)` = 64,409,33994家)
- `sum(actual_food_cost)` = 22,459,07492家,NULL被忽略)
- `sum(operating_expense)` = 34,246,79392家,NULL被忽略)
- 结果 = 7,703,472(含2家无费用门店的实收429万作为"利润"
**正确值**(与 store-expense/overview 一致):6,982,110(仅92家匹配门店)
**影响**:BossPage 利润瀑布图和占比条显示的"门店贡献利润"比指标卡多72万,贡献率高1个百分点。
### 问题2:利润瀑布各项费率分母不一致 ❌
**位置**BossPage.tsx 前端计算 (line 296-318)
**现状**:前端用 `wf.received`64,409,339,94家)做分母计算食材成本率、费用率、贡献率,但 store-expense/overview 用 `matched_received`63,687,97792家)做分母。
| 指标 | 瀑布图口径 | store-expense口径 | 差异 |
|------|-----------|------------------|------|
| 食材成本率 | 34.87% | 35.26% | 0.39pp |
| 费用率 | 53.17% | 53.77% | 0.60pp |
| 贡献率 | 11.96% | 10.96% | 1.00pp |
### 问题3mv_overview_monthly 刷新机制不完整 ⚠️
**现状**`mv_overview_monthly` 是普通表(非物化视图),之前刷新脚本未包含它的更新步骤,导致数据导入后该表不更新。
**已修复**:刷新脚本已加入 DELETE+INSERT 步骤。
---
## 四、修复方案
### 修复问题1+2:统一利润瀑布口径
`/overview/profit-waterfall``store_contribution``received` 改为仅计算有费用门店,与 `/store-expense/overview` 保持一致。
**修改文件**`server/src/routes/data.ts` profit-waterfall API
**修改内容**
1. `received``sum(received) FILTER (WHERE has_expense)`
2. `store_contribution``sum(received) FILTER (WHERE has_expense) - sum(actual_food_cost) - sum(operating_expense)`
3. `consumption``discount` 也改为 `FILTER (WHERE has_expense)` 保持同口径
这样瀑布图的 received、food_cost、expense、contribution 全部基于92家匹配门店,与 store-expense/overview 完全一致。