# 全页面数据溯源审计报告 > 目标:逐页面、逐指标追溯显示名称 → 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/* | --- ## 二、页面1:总部驾驶舱 DashboardPage > 无Tab,用 CollapsibleSection 分区。共调用13个API。 ### 区块①:门店风险分布条 | 显示指标 | 前端变量 | API | API字段 | SQL来源 | 数据源表.字段 | |---------|---------|-----|---------|---------|-------------| | 红色门店数 | `riskSummary['红色']` | /stores/risk | `risk_level` | `SELECT * FROM mv_store_risk_rating_monthly` | analytics.mv_store_risk_rating_monthly.risk_level | | 黄色门店数 | `riskSummary['黄色']` | 同上 | 同上 | 同上 | 同上 | | 绿色门店数 | `riskSummary['绿色']` | 同上 | 同上 | 同上 | 同上 | | 总门店数 | `totalStores = riskRows.length` | 同上 | 行数 | 同上 | 同上 | ### 区块②:态势感知预警 | 显示指标 | 前端变量 | API | API字段 | SQL来源 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 红色预警数 | `alertSummary.red` | /situational-awareness/alerts | `red` | 5类预警合并: 营收异动/成本异动/人力异动/平台依赖/任务逾期 | mv_daily_revenue, dish_cost_analysis_summary, salary_detail_records, mv_store_platform_economics_monthly, store_task | | 橙色预警数 | `alertSummary.orange` | 同上 | `orange` | 同上 | 同上 | | 预警总数 | `alertSummary.total` | 同上 | `total` | 同上 | 同上 | | 预警列表(前6条) | `alerts.slice(0,6)` | 同上 | `alerts[]` | 同上,每条含 title/detail/level | 同上 | ### 区块③:经营指标 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表.字段 | |---------|---------|-----|---------|---------|-------------| | 营业收入 | `od?.consumption` | /overview | `consumption` | `sum(consumption)` | analytics.bill_fact.consumption | | 优惠 | `od?.discount` | /overview | `discount` | `sum(discount_total)` | analytics.bill_fact.discount_total | | 实收 | `od?.received` | /overview | `received` | `sum(received_total)` | analytics.bill_fact.received_total | | 账单数 | `od?.bill_count` | /overview | `bill_count` | `count(*)` | analytics.bill_fact | | 客单价 | `od?.avg_bill_value` | /overview | `avg_bill_value` | `avg(received_total)` | analytics.bill_fact.received_total | | P0+P1门店数 | `p0p1Stores.length` | /stores/priority | 行数过滤 | `action_priority LIKE 'P0%' OR 'P1%'` | analytics.mv_store_action_priority_deep_monthly.action_priority | | 实收环比趋势 | `trendReceived` | /overview/daily | 前端计算 | `(last7days.received - prev7days.received) / prev7days.received * 100` | analytics.mv_overview_daily.received | | 账单数环比趋势 | `trendBills` | /overview/daily | 前端计算 | 同上,用 bill_count | analytics.mv_overview_daily.bill_count | | 客单价环比趋势 | `trendAvgBill` | /overview/daily | 前端计算 | `last7.avg - prev7.avg` | analytics.mv_overview_daily | ### 区块④:运营指标 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表.字段 | |---------|---------|-----|---------|---------|-------------| | 优惠率 | `od?.discount_rate_pct` | /overview | `discount_rate_pct` | `avg(discount_total / consumption * 100)` per bill | analytics.bill_fact.discount_total, consumption | | 理论毛利率 | `od?.theoretical_margin_pct` | /overview | `theoretical_margin_pct` | `sum(theoretical_profit) / sum(received_total) * 100` | analytics.bill_fact.theoretical_profit, received_total | | 会员占比 | `od?.member_share_pct` | /overview | `member_share_pct` | `sum(received_total) FILTER(member_id非空) / sum(received_total) * 100` | analytics.bill_fact.received_total, member_id | ### 区块⑤:同比/环比对比 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 实收同比% | `yoyData_.received_change_pct` | /overview/yoy | `yoy.received_change_pct` | `(当月received - 去年同月received) / |去年同月received| * 100` | analytics.mv_overview_monthly.received | | 实收环比% | `momData_.received_change_pct` | /overview/mom | `mom.received_change_pct` | `(当月received - 上月received) / |上月received| * 100` | analytics.mv_overview_monthly.received | | 账单数同比% | `yoyData_.bill_count_change_pct` | /overview/yoy | `yoy.bill_count_change_pct` | 同上,用 bill_count | analytics.mv_overview_monthly.bill_count | | 账单数环比% | `momData_.bill_count_change_pct` | /overview/mom | `mom.bill_count_change_pct` | 同上 | analytics.mv_overview_monthly.bill_count | | 客单价同比% | `yoyData_.avg_bill_value_change_pct` | /overview/yoy | `yoy.avg_bill_value_change_pct` | 同上,用 avg_bill_value | analytics.mv_overview_monthly.avg_bill_value | | 客单价环比% | `momData_.avg_bill_value_change_pct` | /overview/mom | `mom.avg_bill_value_change_pct` | 同上 | analytics.mv_overview_monthly.avg_bill_value | | 优惠率同比变化 | `yoyData_.discount_rate_change` | /overview/yoy | `yoy.discount_rate_change` | `当月discount_rate_pct - 去年同月discount_rate_pct` | analytics.mv_overview_monthly.discount_rate_pct | | 优惠率环比变化 | `momData_.discount_rate_change` | /overview/mom | `mom.discount_rate_change` | `当月 - 上月` | analytics.mv_overview_monthly.discount_rate_pct | ### 区块⑥:利润与成本 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 理论贡献利润 | `ex.theoretical_net_profit` | /store-expense/overview | `theoretical_net_profit` | `sum(received) FILTER(费用匹配) - sum(theoretical_cost) - sum(operating_expense)` | mv_store_risk_rating_monthly.received + mv_store_operating_expense_monthly | | 理论贡献率 | `ex.theoretical_net_margin_pct` | 同上 | `theoretical_net_margin_pct` | `theoretical_net_profit / matched_received * 100` | 同上 | | 实际贡献利润 | `ex.actual_net_profit` | 同上 | `actual_net_profit` | `sum(received) FILTER(费用匹配) - sum(actual_food_cost) - sum(operating_expense)` | 同上 | | 实际贡献率 | `ex.actual_net_margin_pct` | 同上 | `actual_net_margin_pct` | `actual_net_profit / matched_received * 100` | 同上 | | 盈利门店数 | `ex.profitable_stores` | 同上 | `profitable_stores` | `count(*) FILTER(actual_store_contribution > 0)` | 同上 | | 亏损门店数 | `ex.loss_stores` | 同上 | `loss_stores` | `count(*) FILTER(actual_store_contribution <= 0 AND received > 0)` | 同上 | | 食材成本 | `ex.total_food_cost` | 同上 | `total_food_cost` | `sum(actual_food_cost)` | mv_store_operating_expense_monthly.actual_food_cost | | 实际食材成本率 | `ex.actual_food_cost_rate_pct` | 同上 | `actual_food_cost_rate_pct` | `sum(actual_food_cost) / matched_received * 100` | 同上 | | 理论食材成本率 | `ex.theoretical_food_cost_rate_pct` | 同上 | `theoretical_food_cost_rate_pct` | `sum(theoretical_cost) / matched_received * 100` | 同上 | | 经营费用 | `ex.total_expense` | 同上 | `total_expense` | `sum(operating_expense)` | mv_store_operating_expense_monthly.operating_expense | | 费用率 | `ex.overall_expense_rate_pct` | 同上 | `overall_expense_rate_pct` | `sum(operating_expense) / matched_received * 100` | 同上 | | 人工费用 | `ex.total_wage` | 同上 | `total_wage` | `sum(wage_expense)` | mv_store_operating_expense_monthly.wage_expense | | 人工费率 | `ex.overall_wage_rate_pct` | 同上 | `overall_wage_rate_pct` | `sum(wage_expense) / matched_received * 100` | 同上 | | 房租费用 | `ex.total_rent` | 同上 | `total_rent` | `sum(rent_expense)` | mv_store_operating_expense_monthly.rent_expense | | 房租费率 | `ex.overall_rent_rate_pct` | 同上 | `overall_rent_rate_pct` | `sum(rent_expense) / matched_received * 100` | 同上 | | 水电费用 | `ex.total_utility` | 同上 | `total_utility` | `sum(utility_expense)` | mv_store_operating_expense_monthly.utility_expense | | 水电费率 | `ex.overall_utility_rate_pct` | 同上 | `overall_utility_rate_pct` | `sum(utility_expense) / matched_received * 100` | 同上 | | 外卖佣金 | `ex.total_commission` | 同上 | `total_commission` | `sum(delivery_commission_expense)` | mv_store_operating_expense_monthly.delivery_commission_expense | | 外卖佣金占实收 | 前端计算 | 同上 | 前端除法 | `total_commission / total_received * 100` | 同上 | ### 区块⑦:闭环健康度 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 综合得分 | `loopScore` (前端5项平均) | /tasks/loop-health | 5个rate字段 | analytics.mv_loop_health | | 任务生成率 | `lh.task_generation_rate` | 同上 | `task_generation_rate` | analytics.mv_loop_health.task_generation_rate | | 店长执行率 | `lh.store_execution_rate` | 同上 | `store_execution_rate` | analytics.mv_loop_health.store_execution_rate | | 区域周检率 | `lh.weekly_check_rate` | 同上 | `weekly_check_rate` | analytics.mv_loop_health.weekly_check_rate | | 月度验收率 | `lh.monthly_review_rate` | 同上 | `monthly_review_rate` | analytics.mv_loop_health.monthly_review_rate | | 经验推广率 | `lh.practice_promotion_rate` | 同上 | `practice_promotion_rate` | analytics.mv_loop_health.practice_promotion_rate | ### 区块⑧:图表区 - 日度实收趋势 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 日度实收折线 | `dailyRows[].received` | /overview/daily | `received` | analytics.mv_overview_daily.received (无scope) 或 bill_fact (有scope) | | 日期 | `dailyRows[].business_date` | 同上 | `business_date` | analytics.mv_overview_daily.business_date | ### 区块⑨:图表区 - 门店风险分级分布 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 风险分布饼图 | `riskPieData` | /stores/risk | `risk_level` | analytics.mv_store_risk_rating_monthly.risk_level | ### 区块⑩:图表区 - 门店经营象限 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | X轴: 日均实收 | `quadrantScatterData.x = r.avg_daily_received` | /stores/quadrant | `avg_daily_received` | analytics.mv_store_benchmark_composite_monthly.avg_daily_received | | Y轴: 毛利率 | `quadrantScatterData.y = r.theoretical_margin_pct` | 同上 | `theoretical_margin_pct` | analytics.mv_store_benchmark_composite_monthly.theoretical_margin_pct | | 象限分类 | `r.management_quadrant` | 同上 | `management_quadrant` | analytics.mv_store_benchmark_composite_monthly.management_quadrant | ### 区块⑪:图表区 - P0/P1实收覆盖瀑布图 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | P0门店实收 | `p0Received` | /stores/priority | `received` | `sum(received) FILTER(action_priority LIKE 'P0%')` | analytics.mv_store_action_priority_deep_monthly.received | | P1门店实收 | `p1Received` | /stores/priority | `received` | `sum(received) FILTER(action_priority LIKE 'P1%')` | 同上 | | 其他门店实收 | `otherReceived = od.received - p0Received - p1Received` | /overview + /stores/priority | 前端计算 | `总实收 - P0 - P1` | bill_fact + mv_store_action_priority_deep_monthly | | 合计实收 | `od?.received` | /overview | `received` | `sum(received_total)` | analytics.bill_fact.received_total | ### 区块⑫:图表区 - 平台成本率TOP15 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 美团成本率 | `r.meituan_cost_rate_pct` | /platform/economics | `meituan_cost_rate_pct` | analytics.mv_store_platform_economics_monthly.meituan_cost_rate_pct | | 饿了么成本率 | `r.eleme_cost_rate_pct` | 同上 | `eleme_cost_rate_pct` | analytics.mv_store_platform_economics_monthly.eleme_cost_rate_pct | | 抖音成本率 | `r.douyin_cost_rate_pct` | 同上 | `douyin_cost_rate_pct` | analytics.mv_store_platform_economics_monthly.douyin_cost_rate_pct | ### 区块⑬:月度经营趋势 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 月度实收折线 | `trendRows[].received` | /overview/trend | `received` | analytics.mv_overview_monthly.received | | 月度账单数柱状 | `trendRows[].bill_count` | 同上 | `bill_count` | analytics.mv_overview_monthly.bill_count | ### 区块⑭:利润趋势 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 月度实收 | `profitTrendRows[].received` | /overview/profit-trend | `received` | `sum(r.received)` | v_store_scorecard.received | | 食材成本 | `profitTrendRows[].food_cost` | 同上 | `food_cost` | `sum(COALESCE(e.actual_food_cost, 0))` | mv_store_operating_expense_monthly.actual_food_cost | | 人工 | `profitTrendRows[].wage` | 同上 | `wage` | `sum(COALESCE(e.wage_expense, 0))` | mv_store_operating_expense_monthly.wage_expense | | 房租 | `profitTrendRows[].rent` | 同上 | `rent` | `sum(COALESCE(e.rent_expense, 0))` | mv_store_operating_expense_monthly.rent_expense | | 水电 | `profitTrendRows[].utility` | 同上 | `utility` | `sum(COALESCE(e.utility_expense, 0))` | mv_store_operating_expense_monthly.utility_expense | | 贡献利润 | `profitTrendRows[].store_contribution` | 同上 | `store_contribution` | `sum(r.received) - sum(food_cost) - sum(operating_expense)` | 同上 | > ⚠️ **profit-trend 口径注意**:该API用 `v_store_scorecard` JOIN `mv_store_operating_expense_monthly`,WHERE `e.report_month >= $1 AND <= $2`。LEFT JOIN后WHERE过滤掉无费用门店(e.report_month IS NULL),所以实际只含费用匹配门店,口径与 store-expense/overview 一致。但COALESCE(...,0)是多余的(不会出现NULL行)。 ### 区块⑮:P0/P1重点整改门店列表 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 门店名 | `r.store_name` | /stores/priority | `store_name` | analytics.mv_store_action_priority_deep_monthly.store_name | | 优先级 | `r.action_priority` | 同上 | `action_priority` | 同上 | | 问题数 | `r.problem_count` | 同上 | `problem_count` | 同上 | | 问题组合 | `r.problem_combination` | 同上 | `problem_combination` | 同上 | | 实收 | `r.received` | 同上 | `received` | 同上 | --- ## 三、跨页面核心指标溯源与口径对比 ### 3.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` 的月度汇总。 ### 3.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` 也一致) ### 3.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),因为后者物化视图就来自前者。 ### 3.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。 ### 3.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 | **口径:一致 ✅** — 数学结果相同。 ### 3.6 优惠率 | 页面 | 前端变量 | API | API字段 | 计算方式 | |------|---------|-----|---------|---------| | 总部驾驶舱 | `od?.discount_rate_pct` | /overview | `discount_rate_pct` | `avg(discount_total/consumption*100)` from bill_fact = 19.52% | | 老板驾驶舱 | 无(不显示此指标) | - | - | - | **口径:N/A** — 仅总部驾驶舱显示。 ### 3.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) ### 3.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) ### 3.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) FILTER(has_expense) - sum(food_cost) - sum(operating_expense)` | 6,982,110.42 | **口径:一致 ✅**(已修复)— 瀑布图已改为仅含 has_expense 门店。 ### 3.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 | 前端除法 | `6982110/63687977*100` | 10.96% | **口径:一致 ✅**(已修复)— 瀑布图 received 和 contribution 均改为仅含 has_expense 门店。 ### 3.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,结果相同。 ### 3.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/63687977*100` | 35.26% | **口径:一致 ✅**(已修复)— 瀑布图 received 改为 matched 口径。 ### 3.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,结果相同。 ### 3.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/63687977*100` | 53.77% | | 费用总览Tab | `ov.overall_expense_rate_pct` | /store-expense/overview | `overall_expense_rate_pct` | 同总部 | 53.77% | **口径:一致 ✅**(已修复)— 瀑布图分母改为 matched 口径。 ### 3.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结果相同。 **费率口径:一致 ✅**(已修复)— 瀑布图 received 改为 matched 口径后,前端除法分母一致。 ### 3.16 房租费用 / 房租费率 同 3.15,金额一致,费率已修复一致。 ### 3.17 水电费用 / 水电费率 同 3.15,金额一致,费率已修复一致。 ### 3.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))` | **口径:一致 ✅** — 金额相同。 ### 3.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家。 ### 3.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家。 ### 3.21 门店风险分布 | 页面 | 前端变量 | API | 数据源 | |------|---------|-----|--------| | 总部驾驶舱 | `riskSummary` | /stores/risk | analytics.mv_store_risk_rating_monthly.risk_level | | 老板驾驶舱 | `riskSummary` | /stores/risk | 同上 | **口径:一致 ✅** ### 3.22 P0/P1 门店 | 页面 | 前端变量 | API | 数据源 | |------|---------|-----|--------| | 总部驾驶舱 | `priorityRows` | /stores/priority | analytics.mv_store_action_priority_deep_monthly | | 老板驾驶舱 | `priorityRows` | /stores/priority | 同上 | **口径:一致 ✅** ### 3.23 成本异常分布 | 页面 | 前端变量 | API | 数据源 | |------|---------|-----|--------| | 老板驾驶舱 | `costOv.red_count` 等 | /cost-analysis/store-overview | analytics.mv_store_theoretical_actual_cost_monthly | | 成本分析Tab | 同上 | 同上 | 同上 | **口径:一致 ✅** ### 3.24 利润机会池 | 页面 | 前端变量 | API | 数据源 | |------|---------|-----|--------| | 老板驾驶舱 | `profitOpp` | /overview/profit-opportunity | analytics.mv_store_risk_rating_monthly + mv_store_operating_expense_monthly | **口径:N/A** — 仅老板驾驶舱显示。 ### 3.25 日度趋势 | 页面 | 前端变量 | API | 数据源 | |------|---------|-----|--------| | 总部驾驶舱 | `dailyRows` | /overview/daily | analytics.mv_overview_daily 或 bill_fact | | 老板驾驶舱 | `dailyRows` | /overview/daily | 同上 | **口径:一致 ✅** --- ## 三、发现的问题汇总 ### 问题1:利润瀑布 store_contribution 口径不一致 ✅ 已修复 **位置**:`/overview/profit-waterfall` API (`data.ts:1082`) **已修复**:received/consumption/discount/store_contribution 均改为 `FILTER (WHERE has_expense)`,仅含92家匹配门店,与 store-expense/overview 一致。 ### 问题2:利润瀑布各项费率分母不一致 ✅ 已修复 **已修复**:瀑布图 received 改为 matched 口径后,前端除法分母与 store-expense/overview 一致。 ### 问题3:mv_overview_monthly 刷新机制不完整 ✅ 已修复 **现状**:`mv_overview_monthly` 是普通表(非物化视图),之前刷新脚本未包含它的更新步骤,导致数据导入后该表不更新。 **已修复**:刷新脚本已加入 DELETE+INSERT 步骤。 ### 问题4:KPI达成率利润口径不一致 ⚠️ 待确认 **位置**:`/analytics-enhanced/kpi` API (`analytics-enhanced.ts:944-953`) **现状**: - `actual_revenue = sum(r.received)` — 包含全部94家门店 - `actual_profit = sum(e.actual_store_contribution)` — LEFT JOIN后NULL被sum忽略,实际只含92家有费用门店 - `profit_margin_pct = actual_profit / actual_revenue * 100` — 分子92家/分母94家,偏低 **影响**:利润率指标偏低(分母多含2家无费用门店的实收429万),但影响较小(~0.7pp)。 ### 问题5:store-profit-ranking 用COALESCE导致无费用门店排名异常 ⚠️ 待确认 **位置**:`/overview/store-profit-ranking` API (`data.ts:1094-1112`) **现状**:`store_contribution = r.received - COALESCE(e.actual_food_cost,0) - COALESCE(e.operating_expense,0)`,2家无费用门店的贡献利润 = received - 0 - 0 = received(全部实收算作利润),会排在利润TOP5最前面。 **影响**:BossPage利润排名TOP5可能包含无费用门店,显示"贡献利润=实收"的异常数据。 ### 问题6:StorePage公司均值硬编码 ⚠️ 待确认 **位置**:`StorePage.tsx:188` — `companyAvg` 对象 **现状**:8个指标的公司均值全部硬编码(日均营收20144、客单价36.8、优惠率20.5%等),不随数据更新而变化。 **影响**:如果实际公司均值发生变化,偏差对比将不准确。建议改为API动态获取。 ### 问题7:StorePage scorecard 直接查bill_records而非物化视图 ⚠️ 待确认 **位置**:`/stores/:code` API (`data.ts:177-196`) **现状**:scorecard 直接从 `bill_records` 原始表聚合查询,而其他页面用 `mv_store_risk_rating_monthly` 物化视图。 **影响**:两表同月数据应一致,但若物化视图刷新不及时可能存在差异。且直接查原始表性能较差。 --- ## 四、页面2:老板驾驶舱 BossPage > 无Tab,用区块布局。共调用10个API + KPISection组件1个API。 ### 区块①:核心经营指标(6个MetricCard) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 营业收入 | `od?.consumption` | /overview | `consumption` | `sum(consumption)` | analytics.bill_fact.consumption | | 优惠 | `od?.discount` | /overview | `discount` | `sum(discount_total)` | analytics.bill_fact.discount_total | | 实收总额 | `od?.received` | /overview | `received` | `sum(received_total)` | analytics.bill_fact.received_total | | 实收环比趋势 | `trendReceived` | /overview/daily | 前端计算 | `(last7.received - prev7.received) / prev7.received * 100` | analytics.mv_overview_daily.received | | 门店贡献利润估算 | `ex?.actual_net_profit` | /store-expense/overview | `actual_net_profit` | `sum(received) FILTER(费用匹配) - sum(food_cost) - sum(operating_expense)` | mv_store_risk_rating_monthly + mv_store_operating_expense_monthly | | 贡献率(描述) | `ex?.actual_net_margin_pct` | /store-expense/overview | `actual_net_margin_pct` | `actual_net_profit / matched_received * 100` | 同上 | | 门店数 | `ex?.total_stores` | /store-expense/overview | `total_stores` | `count(*)` | mv_store_risk_rating_monthly | | 盈利门店数(描述) | `ex?.profitable_stores` | /store-expense/overview | `profitable_stores` | `count(*) FILTER(actual_store_contribution > 0)` | 同上 | | 亏损门店数(描述) | `ex?.loss_stores` | /store-expense/overview | `loss_stores` | `count(*) FILTER(<=0 AND received>0)` | 同上 | | 客单价 | `ex?.avg_bill_value` | /store-expense/overview | `avg_bill_value` | `sum(received) / sum(bill_count)` | 同上 | | 客单价环比趋势 | 前端计算 | /overview/daily | 前端计算 | `last7.avg - prev7.avg` | analytics.mv_overview_daily | | 账单数(描述) | `ex?.total_bills` | /store-expense/overview | `total_bills` | `sum(bill_count)` | mv_store_risk_rating_monthly.bill_count | ### 区块①b:KPI达成率(KPISection组件) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 收入达成率 | `kpi.revenue_achievement_pct` | /analytics-enhanced/kpi | `revenue_achievement_pct` | `sum(r.received) / sum(t.revenue_target) * 100` | mv_store_risk_rating_monthly.received + dim_store_target.revenue_target | | 利润达成率 | `kpi.profit_achievement_pct` | 同上 | `profit_achievement_pct` | `sum(e.actual_store_contribution) / sum(t.profit_target) * 100` | mv_store_operating_expense_monthly.actual_store_contribution + dim_store_target.profit_target | | 目标收入 | `kpi.revenue_target` | 同上 | `revenue_target` | `sum(t.revenue_target)` | dim_store_target.revenue_target | | 利润率 | `kpi.profit_margin_pct` | 同上 | `profit_margin_pct` | `sum(actual_store_contribution) / sum(received) * 100` | 同上 | | 目标利润(图表) | `kpi.profit_target` | 同上 | `profit_target` | `sum(t.profit_target)` | dim_store_target.profit_target | | 实际收入(图表) | `kpi.actual_revenue` | 同上 | `actual_revenue` | `sum(r.received)` | mv_store_risk_rating_monthly.received | | 实际利润(图表) | `kpi.actual_profit` | 同上 | `actual_profit` | `sum(e.actual_store_contribution)` | mv_store_operating_expense_monthly.actual_store_contribution | > ⚠️ **KPI口径注意**:`actual_profit` 用 `sum(e.actual_store_contribution)`,LEFT JOIN 费用表后无费用门店的 `actual_store_contribution` 为 NULL,`sum(NULL)` 忽略,所以实际只含有费用的门店。但 `actual_revenue` 用 `sum(r.received)` 包含全部94家。利润率分母含全部门店收入,分子仅含费用匹配门店利润,可能偏低。 ### 区块②:利润结构(瀑布图) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 营业收入 | `wf.consumption` | /overview/profit-waterfall | `consumption` | `sum(consumption) FILTER(has_expense)` | bill_fact.consumption | | 优惠 | `wf.discount` | 同上 | `discount` | `sum(discount) FILTER(has_expense)` | bill_fact.discount_total | | 实收 | `wf.received` | 同上 | `received` | `sum(received) FILTER(has_expense)` | mv_store_risk_rating_monthly.received | | 食材成本 | `wf.food_cost` | 同上 | `food_cost` | `sum(actual_food_cost)` | mv_store_operating_expense_monthly.actual_food_cost | | 人工 | `wf.wage` | 同上 | `wage` | `sum(wage_expense)` | mv_store_operating_expense_monthly.wage_expense | | 房租 | `wf.rent` | 同上 | `rent` | `sum(rent_expense)` | mv_store_operating_expense_monthly.rent_expense | | 水电 | `wf.utility` | 同上 | `utility` | `sum(utility_expense)` | mv_store_operating_expense_monthly.utility_expense | | 宿舍 | `wf.dorm` | 同上 | `dorm` | `sum(dorm_expense)` | mv_store_operating_expense_monthly.dorm_expense | | 外卖佣金 | `wf.commission` | 同上 | `commission` | `sum(delivery_commission_expense)` | mv_store_operating_expense_monthly.delivery_commission_expense | | 其他费用 | `wf.other_expense` | 同上 | `other_expense` | `sum(card_fee + repair_clean)` | mv_store_operating_expense_monthly.card_fee_expense + repair_clean_expense | | 门店贡献利润 | `wf.store_contribution` | 同上 | `store_contribution` | `sum(received) FILTER(has_expense) - sum(food_cost) - sum(operating_expense)` | 同上 | | 食材成本率(占比条) | 前端计算 | 同上 | 前端除法 | `wf.food_cost / wf.received * 100` | 同上 | | 人工费率(占比条) | 前端计算 | 同上 | 前端除法 | `wf.wage / wf.received * 100` | 同上 | | 房租费率(占比条) | 前端计算 | 同上 | 前端除法 | `wf.rent / wf.received * 100` | 同上 | | 水电费率(占比条) | 前端计算 | 同上 | 前端除法 | `wf.utility / wf.received * 100` | 同上 | | 其他费率(占比条) | 前端计算 | 同上 | 前端除法 | `(dorm+commission+other) / wf.received * 100` | 同上 | | 贡献率(占比条) | 前端计算 | 同上 | 前端除法 | `wf.store_contribution / wf.received * 100` | 同上 | | 费用率(汇总) | 前端计算 | 同上 | 前端除法 | `wf.total_expense / wf.received * 100` | 同上 | > ✅ **口径已修复**:所有指标均基于 `FILTER(has_expense)` 的92家门店,与 store-expense/overview 一致。 ### 区块②b:利润机会池 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 机会类别 | `o.category` | /overview/profit-opportunity | `items[].category` | 5类: 食材成本差异/人工优化/水电节能/优惠管控/外卖佣金/SKU精简 | 多表 | | 机会金额 | `o.opportunity` | 同上 | `items[].opportunity` | 各类不同公式,如食材=diff*30% | mv_store_risk_rating_monthly + mv_store_operating_expense_monthly + mv_dish_sku_abc_monthly | | 基线值 | `o.baseline` | 同上 | `items[].baseline` | 各类基线值 | 同上 | | 信心度 | `o.confidence` | 同上 | `items[].confidence` | 中高/中/低 | — | | 责任方 | `o.owner` | 同上 | `items[].owner` | 静态文本 | — | | 验收标准 | `o.evidence` | 同上 | `items[].evidence` | 静态文本 | — | | 详情 | `o.detail` | 同上 | `items[].detail` | 动态拼接TOP5门店数据 | 同上 | | 机会合计 | `totalOpportunity` | 前端计算 | — | `sum(items[].opportunity)` | 同上 | | 30天承诺值 | 静态文本 | — | — | `≥ 150万元`(硬编码) | — | | 占比 | `pct` | 前端计算 | — | `opp / totalOpportunity * 100` | 同上 | ### 区块③:风险态势(3个卡片) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 红色门店数 | `riskSummary['红色']` | /stores/risk | `risk_level` count | `count(*) FILTER(risk_level='红色')` | mv_store_risk_rating_monthly.risk_level | | 红色门店实收 | `riskRevSummary['红色']` | 同上 | `received` sum | `sum(received) FILTER(risk_level='红色')` | mv_store_risk_rating_monthly.received | | 红色实收占比 | 前端计算 | 同上 | 前端除法 | `riskRevSummary['红色'] / od.received * 100` | 同上 | | 黄色门店数 | `riskSummary['黄色']` | 同上 | 同上 | 同上 | 同上 | | 黄色门店实收 | `riskRevSummary['黄色']` | 同上 | 同上 | 同上 | 同上 | | 黄色实收占比 | 前端计算 | 同上 | 前端除法 | 同上 | 同上 | | 绿色门店数 | `riskSummary['绿色']` | 同上 | 同上 | 同上 | 同上 | | 绿色门店实收 | `riskRevSummary['绿色']` | 同上 | 同上 | 同上 | 同上 | | 绿色实收占比 | 前端计算 | 同上 | 前端除法 | 同上 | 同上 | > ✅ **口径已修复**:实收占比分母用 `od.received`(/overview API),与DashboardPage一致。 ### 区块③b:重点整改门店清单 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 门店名 | `s.store_name` | /stores/priority | `store_name` | mv_store_action_priority_deep_monthly.store_name | | 实收 | `s.received` | 同上 | `received` | 同上.received | | 优先级 | `s.action_priority` | 同上 | `action_priority` | 同上.action_priority | | 风险等级 | `s.risk_level` | 同上 | `risk_level` | 同上.risk_level | | 问题组合 | `s.problem_combination` | 同上 | `problem_combination` | 同上.problem_combination | ### 区块④:成本异常分布 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 严重超耗门店数 | `costOv.red_count` | /cost-analysis/store-overview | `red_count` | `count(*) FILTER(variance_level='红色-严重超耗')` | mv_store_theoretical_actual_cost_monthly.variance_level | | 明显超耗门店数 | `costOv.orange_count` | 同上 | `orange_count` | `count(*) FILTER(variance_level='橙色-明显超耗')` | 同上 | | 基本正常门店数 | `costOv.green_count` | 同上 | `green_count` | `count(*) FILTER(variance_level='绿色-基本正常')` | 同上 | | 总差异金额 | `costOv.total_variance` | 同上 | `total_variance` | `sum(food_cost_variance)` | mv_store_theoretical_actual_cost_monthly.food_cost_variance | ### 区块④b:费用结构(饼图) | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 费用科目名 | `r.account_name` | /store-expense/expense-structure | `account_name` | v_operating_expense_account_monthly.account_name | | 费用金额 | `r.amount` | 同上 | `amount` | v_operating_expense_account_monthly.amount | | 费用占比 | `r.expense_share_pct` | 同上 | `expense_share_pct` | v_operating_expense_account_monthly.expense_share_pct | ### 区块⑤:门店营收排名 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | TOP5门店名 | `r.store_name` | /stores/risk | `store_name` | 前端按 received 降序取前5 | mv_store_risk_rating_monthly | | TOP5实收 | `r.received` | 同上 | `received` | 同上 | mv_store_risk_rating_monthly.received | | BOTTOM5门店名 | 同上 | 同上 | 同上 | 前端按 received 升序取末5(received>0) | 同上 | | BOTTOM5实收 | 同上 | 同上 | 同上 | 同上 | 同上 | ### 区块⑤b:门店利润排名 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店名 | `s.store_name` | /overview/store-profit-ranking | `store_name` | — | mv_store_risk_rating_monthly.store_name | | 实收 | `s.received` | 同上 | `received` | `r.received` | mv_store_risk_rating_monthly.received | | 贡献利润 | `s.store_contribution` | 同上 | `store_contribution` | `r.received - COALESCE(e.actual_food_cost,0) - COALESCE(e.operating_expense,0)` | 同上 + mv_store_operating_expense_monthly | | 贡献率 | `s.contribution_margin_pct` | 同上 | `contribution_margin_pct` | `store_contribution / received * 100` | 同上 | > ⚠️ **store-profit-ranking 口径注意**:用 `COALESCE(...,0)` 处理无费用门店,2家无费用门店的贡献利润 = received - 0 - 0 = received(全部实收算作利润),这会导致排名中这2家门店排在最前面。与 store-expense/overview 的 `actual_net_profit` 口径不一致。 ### 区块⑥:日度实收趋势 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 日度实收折线 | `dailyRows[].received` | /overview/daily | `received` | analytics.mv_overview_daily.received | | 日期 | `dailyRows[].business_date` | 同上 | `business_date` | analytics.mv_overview_daily.business_date | | 环比上周% | `trendReceived` | 前端计算 | — | `(last7 - prev7) / prev7 * 100` | ### 区块⑦:本月经营态势弹窗(BusinessReportDialog) > 该弹窗接收 BossPage 已加载的数据(od, ex, wf, riskRows, priorityRows, costOv, profitOpp, trendReceived)进行语音播报,不额外调用API。仅调用 `/tts` API进行语音合成。 --- ## 五、页面3:门店工作台 StorePage > 7个Tab + 概览折叠区。共调用13个API(含KPISection组件1个)。 ### 概览折叠区(门店概况与经营概览) #### 门店概况(4个卡片) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店名称 | `storeName` | /stores/risk | `store_name` | — | mv_store_risk_rating_monthly.store_name | | 风险等级 | `storeInfo.risk` | 同上 | `risk_level` | — | mv_store_risk_rating_monthly.risk_level | | 主要问题 | `primaryIssue` | /stores/priority | `problem_combination` | — | mv_store_action_priority_deep_monthly.problem_combination | | 任务完成率 | 前端计算 | /tasks | `status` count | `doneTasks / tasks * 100` | analytics.store_task.status | | 已完成/总数(描述) | 前端计算 | 同上 | 同上 | `doneTasks.length / tasks.length` | 同上 | #### KPI达成率(KPISection组件,level=store) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 收入达成率 | `kpi.revenue_achievement_pct` | /analytics-enhanced/kpi | `revenue_achievement_pct` | `r.received / t.revenue_target * 100` | mv_store_risk_rating_monthly.received + dim_store_target.revenue_target | | 利润达成率 | `kpi.profit_achievement_pct` | 同上 | `profit_achievement_pct` | `e.actual_store_contribution / t.profit_target * 100` | mv_store_operating_expense_monthly.actual_store_contribution + dim_store_target.profit_target | | 目标收入 | `kpi.revenue_target` | 同上 | `revenue_target` | `t.revenue_target` | dim_store_target.revenue_target | | 利润率 | `kpi.profit_margin_pct` | 同上 | `profit_margin_pct` | `e.actual_store_contribution / r.received * 100` | 同上 | #### 最新经营日概览(daily-card) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 营业收入(当日) | `item.value`(metric=营业收入) | /tasks/stores/:code/daily-card | `anomalies[].value` | `sum(c009)` 当日 | bill_records.c009 | | 营业收入基准 | `item.baseline` | 同上 | `anomalies[].baseline` | `sum(c009)` 一周前同日 | bill_records.c009 | | 营业收入异常 | `item.is_anomaly` | 同上 | `anomalies[].is_anomaly` | `value < baseline * 0.8` | — | | 优惠(当日) | `item.value`(metric=优惠) | 同上 | 同上 | `sum(c068)` 当日 | bill_records.c068 | | 优惠异常 | 同上 | 同上 | 同上 | `value > baseline * 1.2` | — | | 实收(当日) | `item.value`(metric=实收) | 同上 | 同上 | `sum(c114)` 当日 | bill_records.c114 | | 实收异常 | 同上 | 同上 | 同上 | `value < baseline * 0.8` | — | | 账单数(当日) | `item.value`(metric=账单数) | 同上 | 同上 | `count(*)` 当日 | bill_records | | 账单数异常 | 同上 | 同上 | 同上 | `value < baseline * 0.8` | — | | 客单价(当日) | `item.value`(metric=客单价) | 同上 | 同上 | `sum(c114)/count(*)` 当日 | bill_records | | 客单价异常 | 同上 | 同上 | 同上 | `value < baseline * 0.9` | — | | 异常项数 | `anomalyCount` | 前端计算 | — | `count(is_anomaly=true)` | — | | 对比日期 | `card.target_date` / `card.baseline_date` | 同上 | `target_date` / `baseline_date` | `max(c176::date)` / `target_date - 7天` | bill_records.c176 | #### 本周指标进度(followup) | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 实收(基线/目标/实际) | `followup.baseline_received` / `target_received` / `actual_received` | /tasks/followup | 同名 | analytics.mv_store_monthly_followup | | 客单价(基线/目标/实际) | `followup.baseline_avg_bill` / `target_avg_bill` / `actual_avg_bill` | 同上 | 同名 | 同上 | | 优惠率(基线/目标/实际) | `followup.baseline_discount_rate` / `target_discount_rate` / `actual_discount_rate` | 同上 | 同名 | 同上 | | 毛利率(基线/目标/实际) | `followup.baseline_margin_rate` / `target_margin_rate` / `actual_margin_rate` | 同上 | 同名 | 同上 | | 达成率 | 前端计算 | — | — | `actual / target * 100` | | 异常率(基线/目标/实际) | `followup.baseline_anomaly_rate` / `target_anomaly_rate` / `actual_anomaly_rate` | 同上 | 同名 | 同上 | | 会员占比(基线/目标/实际) | `followup.baseline_member_share` / `target_member_share` / `actual_member_share` | 同上 | 同名 | 同上 | | 工作重点 | `followup.work_focus` | 同上 | `work_focus` | 同上 | | 行动计划 | `followup.action_plan` | 同上 | `action_plan` | 同上 | #### 日度实收趋势 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 日度实收折线 | `dailyRows[].received` | /stores/:code/daily | `received` | bill_records.c114 (按c176::date分组) | | 日期 | `dailyRows[].business_date` | 同上 | `business_date` | bill_records.c176::date | #### 核心SKU备货提醒 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | SKU名称 | `s.dish_name` | /sku/abc | `dish_name` | analytics.mv_dish_sku_abc_monthly.dish_name | | ABC等级 | `s.abc_class` | 同上 | `abc_class` | 同上.abc_class | | 营收占比 | `s.revenue_share_pct` | 同上 | `revenue_share_pct` | 同上.revenue_share_pct | | 累计占比 | `s.cumulative_revenue_share_pct` | 同上 | `cumulative_revenue_share_pct` | 同上.cumulative_revenue_share_pct | > 前端过滤 `abc_class === 'A'` 取前10条。 #### 待办/已完成任务 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 优先级 | `t.priority` | /tasks | `priority` | analytics.store_task.priority | | 问题指标 | `t.problem_indicator` | 同上 | `problem_indicator` | 同上.problem_indicator | | 状态 | `t.status` | 同上 | `status` | 同上.status | | 行动要求 | `t.action_required` | 同上 | `action_required` | 同上.action_required | | 截止日 | `t.deadline` | 同上 | `deadline` | 同上.deadline | | 验收结果 | `t.verification_result` | 同上 | `verification_result` | 同上.verification_result | | 执行证据 | `t.process_evidence` | 同上 | `process_evidence` | 同上.process_evidence | | 后续计划 | `t.next_step` | 同上 | `next_step` | 同上.next_step | ### 核心指标行(8个MetricCard,scorecard) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 营业收入 | `sc.consumption` | /stores/:code | `scorecard.consumption` | `sum(c009)` | bill_records.c009 | | 优惠 | `sc.discount` | 同上 | `scorecard.discount` | `sum(c068)` | bill_records.c068 | | 月度实收 | `sc.received` | 同上 | `scorecard.received` | `sum(c114)` | bill_records.c114 | | 月度账单数 | `sc.bill_count` | 同上 | `scorecard.bill_count` | `count(*)` | bill_records | | 客单价 | `sc.avg_bill_value` | 同上 | `scorecard.avg_bill_value` | `sum(c114)/count(*)` | bill_records | | 优惠率 | `sc.discount_rate_pct` | 同上 | `scorecard.discount_rate_pct` | `sum(c068)/sum(c009)*100` | bill_records | | 理论毛利率 | `sc.theoretical_margin_pct` | 同上 | `scorecard.theoretical_margin_pct` | `sum(c181)/sum(c114)*100` | bill_records.c181/c114 | | 会员占比 | `sc.member_bill_share_pct` | 同上 | `scorecard.member_bill_share_pct` | `count(c185非空)/count(*)*100` | bill_records.c185 | > ⚠️ **scorecard数据源注意**:`/stores/:code` 的 scorecard 直接从 `bill_records` 原始表查询(非物化视图),而其他页面用 `mv_store_risk_rating_monthly`。两表同月数据应一致,但若刷新不及时可能存在差异。 ### 风险与经营状态说明 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 风险等级 | `risk.risk_level` | /stores/:code | `risk.risk_level` | mv_store_risk_rating_monthly.risk_level | | 问题组合 | `action.problem_combination` | 同上 | `action.problem_combination` | mv_store_action_priority_deep_monthly.problem_combination | | 经营象限 | `action.management_quadrant` | 同上 | `action.management_quadrant` | 后端计算:日均营收 vs 中位18642 & 毛利率 vs 中位70% | | 规模层级 | `action.scale_tier` | 同上 | `action.scale_tier` | mv_store_action_priority_deep_monthly.scale_tier | | 综合基准分 | `action.benchmark_score` | 同上 | `benchmark.benchmark_score` | mv_store_benchmark_composite_monthly | | 行动优先级 | `action.action_priority` | 同上 | `action.action_priority` | mv_store_action_priority_deep_monthly.action_priority | | 成本差异率 | `action.variance_to_theoretical_pct` | 同上 | `action.variance_to_theoretical_pct` | 同上.variance_to_theoretical_pct | | 成本差异等级 | `action.variance_level` | 同上 | `action.variance_level` | 同上.variance_level | #### 月度指标偏差对比(vs 公司均值,硬编码) | 显示指标 | 前端变量 | 门店值来源 | 公司均值 | 数据源表 | |---------|---------|-----------|---------|---------| | 日均营收 | `risk.avg_daily_received` | /stores/:code → risk | 20144(硬编码) | mv_store_risk_rating_monthly.avg_daily_received | | 客单价 | `risk.avg_bill_value` | 同上 | 36.8(硬编码) | 同上.avg_bill_value | | 优惠率 | `risk.discount_rate_pct` | 同上 | 20.5(硬编码) | 同上.discount_rate_pct | | 毛利率 | `risk.theoretical_margin_pct` | 同上 | 71.5(硬编码) | 同上.theoretical_margin_pct | | 会员占比 | `risk.member_bill_share_pct` | 同上 | 15.9(硬编码) | 同上.member_bill_share_pct | | 复购率 | `action.repeat_rate_pct` | 同上 → action | 37.0(硬编码) | mv_store_action_priority_deep_monthly.repeat_rate_pct | | 外卖占比 | `action.delivery_bill_share_pct` | 同上 | 36.1(硬编码) | 同上.delivery_bill_share_pct | | 成本差异率 | `action.variance_to_theoretical_pct` | 同上 | 20(硬编码) | 同上.variance_to_theoretical_pct | > ⚠️ **公司均值硬编码**:`companyAvg` 对象中的值是硬编码的,不随数据更新而变化。如果实际公司均值发生变化,偏差对比将不准确。 --- ### Tab1:概览 #### 健康度评分维度(雷达图 + 进度条) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 健康总分 | `health.health_score` | /situational-awareness/health-score | `health_score` | 8维加权总分(0-100) | 多表联合计算 | | 营收规模得分 | `health.score_revenue` | 同上 | `score_revenue` | `20 * LEAST(1.0, avg_daily_received/30000)` | mv_store_risk_rating_monthly.avg_daily_received | | 成本控制得分 | `health.score_cost` | 同上 | `score_cost` | `20 * GREATEST(0, 1 - LEAST(variance,50)/50)` | mv_store_theoretical_actual_cost_monthly.variance_to_theoretical_pct | | 毛利率得分 | `health.score_margin` | 同上 | `score_margin` | `15 * LEAST(1.0, GREATEST(0, margin-60)/15)` | mv_store_risk_rating_monthly.theoretical_margin_pct | | 风险控制得分 | `health.score_risk` | 同上 | `score_risk` | 绿色=10, 黄色=6, 红色=2 | mv_store_risk_rating_monthly.risk_level | | 复购率得分 | `health.score_repeat` | 同上 | `score_repeat` | `10 * LEAST(1.0, repeat_rate/40)` | mv_store_repeat_summary_monthly.repeat_rate_pct | | 会员占比得分 | `health.score_member` | 同上 | `score_member` | `10 * LEAST(1.0, member_share/20)` | mv_store_risk_rating_monthly.member_bill_share_pct | | 任务完成得分 | `health.score_task` | 同上 | `score_task` | `10 * completion_rate/100` | analytics.store_task | | 客单价得分 | `health.score_bill` | 同上 | `score_bill` | `5 * LEAST(1.0, avg_bill_value/40)` | mv_store_risk_rating_monthly.avg_bill_value | | 健康状态 | `health.health_status` | 同上 | `health_status` | ≥75=健康, ≥55=亚健康, <55=需干预 | — | #### 平台经济性 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 美团实收 | `sd.platform.meituan_received` | /stores/:code | `platform.meituan_received` | mv_store_platform_economics_monthly.meituan_received | | 饿了么实收 | `sd.platform.eleme_received` | 同上 | `platform.eleme_received` | 同上.eleme_received | | 抖音实收 | `sd.platform.douyin_received` | 同上 | `platform.douyin_received` | 同上.douyin_received | | 平台加权成本率 | `sd.platform.weighted_cost_rate_pct` | 同上 | `platform.weighted_cost_rate_pct` | 同上.weighted_cost_rate_pct | --- ### Tab2:餐段分析 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 餐段 | `mealRows[].meal_period` | /stores/:code/meal-period | `meal_period` | — | analytics.bill_fact.meal_period | | 账单数 | `mealRows[].bill_count` | 同上 | `bill_count` | `count(*)` | analytics.bill_fact | | 实收 | `mealRows[].received` | 同上 | `received` | `sum(received_total)` | analytics.bill_fact.received_total | | 平均单价 | `mealRows[].avg_bill` | 同上 | `avg_bill` | `sum(received_total)/count(*)` | 同上 | | 全网均价 | `mealRows[].network_avg_bill` | 同上 | `network_avg_bill` | 全门店同餐段 `sum(received)/count(*)` | analytics.bill_fact | | vs全网% | `mealRows[].vs_network_pct` | 同上 | `vs_network_pct` | `(store.avg / network.avg - 1) * 100` | — | --- ### Tab3:品类结构 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店各品类金额 | `s.lanzhou_noodle` 等 | /stores/:code/category-mix | `store.*` | 各品类 `sum(consumption)` | mv_store_category_mix_monthly | | 公司各品类金额 | `c.total_noodle` 等 | 同上 | `company.*` | 全门店 `sum(各品类)` | 同上 | | 门店品类占比 | 前端计算 | — | — | `品类金额 / 门店总金额 * 100` | — | | 公司品类占比 | 前端计算 | — | — | `品类金额 / 公司总金额 * 100` | — | | 主力品类 | `s.top_category` | 同上 | `store.top_category` | — | mv_store_category_mix_monthly.top_category | | 主力品类占比 | `s.top_category_share_pct` | 同上 | `store.top_category_share_pct` | — | 同上.top_category_share_pct | | 面食占比 | `s.noodle_share_pct` | 同上 | `store.noodle_share_pct` | — | 同上.noodle_share_pct | | 外卖占比 | `s.delivery_package_share_pct` | 同上 | `store.delivery_package_share_pct` | — | 同上.delivery_package_share_pct | --- ### Tab4:成本分析 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 理论成本率 | `cost.cost.theoretical_cost_rate_pct` | /stores/:code/cost | `cost.theoretical_cost_rate_pct` | — | mv_store_theoretical_actual_cost_monthly | | 实际成本率 | `cost.cost.actual_food_cost_rate_pct` | 同上 | `cost.actual_food_cost_rate_pct` | — | 同上 | | 差异率 | `cost.cost.variance_to_theoretical_pct` | 同上 | `cost.variance_to_theoretical_pct` | — | 同上 | | 差异等级 | `cost.cost.variance_level` | 同上 | `cost.variance_level` | — | 同上 | | 原料分类名 | `cost.categories[].category_name` | 同上 | `categories[].category_name` | `finance_category` | mv_inventory_cost_classified_monthly.finance_category | | 消耗金额 | `cost.categories[].consumption_amount` | 同上 | `categories[].consumption_amount` | `sum(consumption_amount)` | 同上.consumption_amount | | 实际占比 | `cost.categories[].actual_cost_rate_pct` | 同上 | `categories[].actual_cost_rate_pct` | `分类消耗/总消耗*100` | — | | 公司基准 | `cost.categories[].benchmark_rate_pct` | 同上 | `categories[].benchmark_rate_pct` | 全门店同分类占比 | — | | 差异 | `cost.categories[].variance_pct` | 同上 | `categories[].variance_pct` | `actual - benchmark` | — | --- ### Tab5:会员分析 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 会员占比 | `member.opportunity.member_share_pct` | /stores/:code/member | `opportunity.member_share_pct` | — | mv_store_member_opportunity_monthly | | 公司均值会员占比 | `member.opportunity.company_member_share_pct` | 同上 | `opportunity.company_member_share_pct` | — | 同上 | | 复购率 | `member.repeat.repeat_rate_pct` | 同上 | `repeat.repeat_rate_pct` | — | mv_store_repeat_summary_monthly | | 复购收入占比 | `member.repeat.repeat_revenue_share_pct` | 同上 | `repeat.repeat_revenue_share_pct` | — | 同上 | | 活跃会员数 | `member.monthly.member_count` | 同上 | `monthly.member_count` | `count(*)` | v_store_member_monthly_activity | | 总订单数 | `member.monthly.total_orders` | 同上 | `monthly.total_orders` | `sum(orders)` | 同上 | | 人均订单 | `member.monthly.avg_orders` | 同上 | `monthly.avg_orders` | `avg(orders)` | 同上 | | 人均消费 | `member.monthly.avg_received` | 同上 | `monthly.avg_received` | `avg(received)` | 同上 | | 提升机会(文本) | `member.opportunity.conversion_bill_scenario` | 同上 | `opportunity.conversion_bill_scenario` | — | mv_store_member_opportunity_monthly | | 收入提升场景(文本) | `member.opportunity.revenue_uplift_scenario` | 同上 | `opportunity.revenue_uplift_scenario` | — | 同上 | --- ### Tab6:异常账单 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 账单号 | `r.bill_no` | /stores/:code/anomalies | `bill_no` | mv_risk_anomaly.bill_no | | 餐段 | `r.meal_period` | 同上 | `meal_period` | 同上.meal_period | | 消费额 | `r.consumption` | 同上 | `consumption` | 同上.consumption | | 优惠 | `r.discount_total` | 同上 | `discount_total` | 同上.discount_total | | 实收 | `r.received_total` | 同上 | `received_total` | 同上.received_total | | 异常原因 | `r.anomaly_reason` | 同上 | `anomaly_reason` | 同上.anomaly_reason | | 结账时间 | `r.closed_at` | 同上 | `closed_at` | 同上.closed_at | | 总数 | `anomalyDetailTotal` | 同上 | `meta.total` | — | > ⚠️ **anomalies数据源注意**:`/stores/:code/anomalies` 通过 `store_name`(非 `store_code`)关联 `mv_risk_anomaly` 视图,需先查 `dim_store` 获取 `store_name` 再查异常表。 --- ### Tab7:人力排班 #### 人力成本概览(6个MetricCard) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 人工成本 | `staffing.summary.wage_expense` | /stores/:code/staffing | `summary.wage_expense` | — | mv_store_operating_expense_monthly.wage_expense | | 人工成本率 | `staffing.summary.wage_rate_pct` | 同上 | `summary.wage_rate_pct` | — | 同上.wage_rate_pct | | 公司均值人工率 | `staffing.summary.avg_wage_rate` | 同上 | `summary.avg_wage_rate` | `avg(wage_rate_pct)` 排除>100% | 同上 | | 人效 | `staffing.summary.revenue_per_wage` | 同上 | `summary.revenue_per_wage` | `received / wage_expense` | 同上 | | 每单人工成本 | `staffing.summary.wage_per_bill` | 同上 | `summary.wage_per_bill` | `wage_expense / bill_count` | 同上 | | 公司均值人效 | `staffing.summary.avg_rev_per_wage` | 同上 | `summary.avg_rev_per_wage` | `avg(received/wage_expense)` | 同上 | #### 人力成本优化空间 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 建议人工成本 | `staffing.summary.suggested_wage` | 同上 | `summary.suggested_wage` | `received * max(20%, avgWageRate-3%) / 100` | 后端计算 | | 可节省金额 | `staffing.summary.wage_savings` | 同上 | `summary.wage_savings` | `wage_expense - suggested_wage` | 后端计算 | | 节省比例 | `staffing.summary.savings_pct` | 同上 | `summary.savings_pct` | `wage_savings / wage_expense * 100` | 后端计算 | #### 按星期排班分析 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 星期 | `weekdays[].weekday` | 同上 | `weekdays[].weekday` | — | v_store_daily | | 日均实收 | `weekdays[].avg_received` | 同上 | 同上 | `avg(received)` 按星期分组 | v_store_daily.received | | 日均账单 | `weekdays[].avg_bills` | 同上 | 同上 | `avg(bill_count)` | v_store_daily.bill_count | | 最高实收 | `weekdays[].max_received` | 同上 | 同上 | `max(received)` | v_store_daily.received | | 最低实收 | `weekdays[].min_received` | 同上 | 同上 | `min(received)` | v_store_daily.received | | 建议排班比 | `weekdays[].suggested_headcount_ratio` | 同上 | 同上 | `avg_received / 全周日均 * 100` | 后端计算 | | 排班等级 | `weekdays[].staffing_level` | 同上 | 同上 | >110%=高峰, <85%=低谷, else=正常 | — | | 排班建议 | `weekdays[].staffing_action` | 同上 | 同上 | 静态文本 | — | #### 餐段排班分析 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 餐段 | `meals[].meal_period` | 同上 | `meals[].meal_period` | — | v_store_meal_opportunity | | 账单数 | `meals[].bill_count` | 同上 | 同上 | — | 同上 | | 实收 | `meals[].received` | 同上 | 同上 | — | 同上 | | 客单价 | `meals[].avg_bill` | 同上 | 同上 | — | 同上 | | 账单占比 | `meals[].bill_share_pct` | 同上 | 同上 | `餐段账单/总账单*100` | 后端计算 | | 排班等级 | `meals[].staffing_level` | 同上 | 同上 | >40%=主力, >20%=次高峰, >5%=辅助, else=低谷 | — | | 排班建议 | `meals[].staffing_action` | 同上 | 同上 | 静态文本 | — | #### 日营收明细(排班参考) | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 日期 | `daily[].business_date` | 同上 | `daily[].business_date` | v_store_daily.business_date | | 实收 | `daily[].received` | 同上 | 同上 | v_store_daily.received | --- ## 六、页面4:菜品成本 CostAnalysisPage > 10个Tab,每个Tab独立组件。共调用22个API(含1个POST、1个PUT)。 > 核心数据源:`public.dish_cost_analysis_summary`(菜品成本汇总表,按月导入)+ `public.dish_cost_analysis_material_detail`(原料明细表)+ `analytics.fact_recipe_bom`(BOM配方表)。 ### Tab1:成本总览(OverviewTab) #### 概览指标(8个MetricCard) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 总销售额 | `ov.total_sales` | /cost-analysis/overview | `total_sales` | `sum(sales_amount)` | dish_cost_analysis_summary.sales_amount | | 理论成本合计 | `ov.total_theo_cost` | 同上 | `total_theo_cost` | `sum(theoretical_cost)` | 同上.theoretical_cost | | 实际成本合计 | `ov.total_actual_cost` | 同上 | `total_actual_cost` | `sum(actual_cost)` | 同上.actual_cost | | 成本差异 | `ov.total_variance` | 同上 | `total_variance` | `sum(cost_variance_amount)` | 同上.cost_variance_amount | | 平均理论毛利率 | `ov.avg_theo_margin` | 同上 | `avg_theo_margin` | `(1 - sum(theoretical_cost)/sum(sales_amount)) * 100` | 同上 | | 平均实际毛利率 | `ov.avg_actual_margin` | 同上 | `avg_actual_margin` | `(1 - sum(actual_cost)/sum(sales_amount)) * 100` | 同上 | | 低毛利菜品数 | `ov.low_margin_dishes` | 同上 | `low_margin_dishes` | `count(*) FILTER(theoretical_margin_rate_pct < 50)` | 同上.theoretical_margin_rate_pct | | 超理论菜品数 | `ov.over_cost_dishes` | 同上 | `over_cost_dishes` | `count(*) FILTER(cost_variance_amount > 0)` | 同上.cost_variance_amount | > **数据导入机制**:所有查询通过 `import_id` 关联 `dish_cost_analysis_import_log` 取最新月度导入批次。 #### 品类成本对比 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 品类 | `cats[].category_level1` | /cost-analysis/category-comparison | `category_level1` | — | dish_cost_analysis_summary.category_level1 | | 菜品数 | `cats[].dishes` | 同上 | `dishes` | `count(*)` | 同上 | | 理论毛利率 | `cats[].avg_theo_margin` | 同上 | `avg_theo_margin` | `(1 - sum(theoretical_cost)/sum(sales_amount)) * 100` | 同上 | | 实际毛利率 | `cats[].avg_actual_margin` | 同上 | `avg_actual_margin` | `(1 - sum(actual_cost)/sum(sales_amount)) * 100` | 同上 | | 销售额 | `cats[].total_sales` | 同上 | `total_sales` | `sum(sales_amount)` | 同上 | | 成本差异 | `cats[].total_variance` | 同上 | `total_variance` | `sum(cost_variance_amount)` | 同上 | #### 毛利率偏差分布 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 偏差区间 | `devs[].deviation_band` | /cost-analysis/margin-deviation | `deviation_band` | CASE分组:实际-理论偏差 | dish_cost_analysis_summary | | 菜品数 | `devs[].cnt` | 同上 | `cnt` | `count(*)` | 同上 | #### 成本差异TOP榜 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 菜品 | `tops[].dish_name` | /cost-analysis/variance-top | `dish_name` | — | dish_cost_analysis_summary.dish_name | | 品类 | `tops[].category_level1` | 同上 | `category_level1` | — | 同上 | | 理论毛利率 | `tops[].theo_margin` | 同上 | `theo_margin` | `theoretical_margin_rate_pct` | 同上 | | 实际毛利率 | `tops[].actual_margin` | 同上 | `actual_margin` | `actual_margin_rate_pct` | 同上 | | 成本差异 | `tops[].cost_variance` | 同上 | `cost_variance` | `cost_variance_amount` | 同上 | | 销售额 | `tops[].sales_amount` | 同上 | `sales_amount` | — | 同上 | | 分层 | `tops[].cost_tier` | 同上 | `cost_tier` | CASE:>20%=紧急, >10%=整改, >0=关注, else=正常 | — | --- ### Tab2:菜品盈利(ProfitabilityTab) #### 菜单工程矩阵(散点图) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 菜品 | `matrixData[].dish_name` | /cost-analysis/menu-engineering | `dish_name` | — | dish_cost_analysis_summary | | 销量 | `matrixData[].sales_quantity` | 同上 | `sales_quantity` | — | 同上.sales_quantity | | 实际毛利率 | `matrixData[].actual_margin` | 同上 | `actual_margin` | `actual_margin_rate_pct` | 同上 | | 销售额 | `matrixData[].sales_amount` | 同上 | `sales_amount` | — | 同上 | | 菜单类型 | `matrixData[].menu_type` | 同上 | `menu_type` | 双中位分割:销量&毛利率各取中位数 → 明星/高销低利/低销高利/低销低利/数据异常 | — | #### 菜品盈利明细(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 菜品 | `profitData[].dish_name` | /cost-analysis/profitability | `dish_name` | — | dish_cost_analysis_summary | | 品类 | `profitData[].category_level1` | 同上 | `category_level1` | — | 同上 | | 销量 | `profitData[].sales_quantity` | 同上 | `sales_quantity` | — | 同上 | | 销售额 | `profitData[].sales_amount` | 同上 | `sales_amount` | — | 同上 | | 理论毛利 | `profitData[].theo_profit` | 同上 | `theo_profit` | `sales_amount - theoretical_cost` | 同上 | | 实际毛利 | `profitData[].actual_profit` | 同上 | `actual_profit` | `sales_amount - actual_cost` | 同上 | | 理论毛利率 | `profitData[].theo_margin` | 同上 | `theo_margin` | `theoretical_margin_rate_pct` | 同上 | | 实际毛利率 | `profitData[].actual_margin` | 同上 | `actual_margin` | `actual_margin_rate_pct` | 同上 | | 收入贡献度 | `profitData[].revenue_contribution` | 同上 | `revenue_contribution` | `sales_amount / sum(sales_amount) OVER() * 100` | 同上 | #### 低毛利菜品预警 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 菜品 | `pricingData[].dish_name` | /cost-analysis/pricing | `dish_name` | — | dish_cost_analysis_summary | | 售价 | `pricingData[].price` | 同上 | `price` | — | 同上.price | | 理论成本 | `pricingData[].theo_cost` | 同上 | `theo_cost` | `theoretical_cost` | 同上 | | 理论成本率 | `pricingData[].theo_cost_rate` | 同上 | `theo_cost_rate` | `theoretical_cost / price * 100` | 同上 | | 理论毛利率 | `pricingData[].theo_margin` | 同上 | `theo_margin` | `theoretical_margin_rate_pct` | 同上 | | 实际毛利率 | `pricingData[].actual_margin` | 同上 | `actual_margin` | `actual_margin_rate_pct` | 同上 | | 销售额 | `pricingData[].sales_amount` | 同上 | `sales_amount` | — | 同上 | > 筛选条件:`theoretical_margin_rate_pct < 50 AND sales_amount > 1000`。 --- ### Tab3:原料差异(MaterialTab) #### 菜品原料差异分解(按菜品编码查询) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 原料 | `varianceData[].material_name` | /cost-analysis/material-variance | `material_name` | — | dish_cost_analysis_material_detail.material_name | | 单位 | `varianceData[].material_unit` | 同上 | `material_unit` | — | 同上.material_unit | | 理论用量 | `varianceData[].theo_qty` | 同上 | `theo_qty` | `theoretical_quantity` | 同上.theoretical_quantity | | 实际用量 | `varianceData[].actual_qty` | 同上 | `actual_qty` | `actual_quantity` | 同上.actual_quantity | | 数量差异 | `varianceData[].loss_qty` | 同上 | `loss_qty` | `loss_quantity` | 同上.loss_quantity | | 损耗率 | `varianceData[].loss_rate` | 同上 | `loss_rate` | `loss_quantity_rate_pct` | 同上.loss_quantity_rate_pct | | 金额差异 | `varianceData[].loss_amount` | 同上 | `loss_amount` | `loss_amount` | 同上.loss_amount | | 原因 | `varianceData[].reason` | 同上 | `reason` | CASE:<-100%=分摊遗漏, <-30%=份量超标, =0=未使用, else=正常损耗 | — | #### 物料损耗率分布 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 损耗区间 | `distData[].loss_band` | /cost-analysis/loss-distribution | `loss_band` | CASE分档:无损耗/轻微/轻度/中度/严重/极重/正向 | dish_cost_analysis_material_detail | | 明细数 | `distData[].cnt` | 同上 | `cnt` | `count(*)` | 同上 | #### 超耗物料TOP榜 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 物料 | `topData[].material_name` | /cost-analysis/material-loss-top | `material_name` | — | dish_cost_analysis_material_detail | | 类型 | `topData[].material_type` | 同上 | `material_type` | — | 同上 | | 涉及菜品数 | `topData[].dish_count` | 同上 | `dish_count` | `count(DISTINCT summary_id)` | 同上 | | 总损耗量 | `topData[].total_loss_qty` | 同上 | `total_loss_qty` | `sum(loss_quantity)` | 同上 | | 平均损耗率 | `topData[].avg_loss_rate` | 同上 | `avg_loss_rate` | `sum(loss_quantity)/sum(theoretical_quantity)*100` | 同上 | | 最大损耗率 | `topData[].max_loss_rate` | 同上 | `max_loss_rate` | `max(loss_quantity_rate_pct)` | 同上 | | 损耗金额 | `topData[].total_loss_amount` | 同上 | `total_loss_amount` | `sum(loss_amount)` | 同上 | #### 物料类型损耗对比 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 物料类型 | `typeData[].material_type` | /cost-analysis/material-type-loss | `material_type` | — | dish_cost_analysis_material_detail | | 明细数 | `typeData[].cnt` | 同上 | `cnt` | `count(*)` | 同上 | | 物料种类 | `typeData[].unique_materials` | 同上 | `unique_materials` | `count(DISTINCT material_name)` | 同上 | | 平均损耗率 | `typeData[].avg_loss_rate` | 同上 | `avg_loss_rate` | `sum(loss_quantity)/sum(theoretical_quantity)*100` | 同上 | | 损耗金额 | `typeData[].total_loss_amount` | 同上 | `total_loss_amount` | `sum(loss_amount)` | 同上 | --- ### Tab4:BOM与配方(BomTab) #### BOM总览(6个MetricCard) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | BOM覆盖率 | `ov.coverage_pct` | /cost-analysis/bom-overview | `coverage_pct` | `count(DISTINCT sku_code in BOM) / count(dim_sku) * 100` | fact_recipe_bom + dim_sku | | 有BOM的SKU | `ov.sku_with_bom` | 同上 | `sku_with_bom` | `count(DISTINCT sku_code)` | fact_recipe_bom.sku_code | | 缺BOM的SKU | `ov.sku_without_bom` | 同上 | `sku_without_bom` | `total_sku - sku_with_bom` | dim_sku - fact_recipe_bom | | BOM总行数 | `ov.total_bom_rows` | 同上 | `total_bom_rows` | `count(*)` | fact_recipe_bom | | 高损耗BOM数 | `ov.high_loss_bom` | 同上 | `high_loss_bom` | `count(*) FILTER(waste_rate > 20)` | fact_recipe_bom.waste_rate | | 平均物料数/SKU | `ov.avg_materials_per_sku` | 同上 | `avg_materials_per_sku` | `avg(count per sku)` | 同上 | > ⚠️ **bom-overview 无月份参数**:该API不接收month参数,BOM数据不按月区分。 #### BOM复杂度明细(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | SKU编码 | `compData[].sku_code` | /cost-analysis/bom-complexity | `sku_code` | — | fact_recipe_bom.sku_code | | 菜品名 | `compData[].dish_name` | 同上 | `dish_name` | `dim_sku.standard_name` | dim_sku.standard_name | | 品类 | `compData[].category_l1` | 同上 | `category_l1` | — | dim_sku.category_l1 | | 原料数 | `compData[].material_count` | 同上 | `material_count` | `count(*)` | fact_recipe_bom | | 半成品数 | `compData[].semi_finished_count` | 同上 | `semi_finished_count` | `count(*) FILTER(major_category='半成品')` | fact_recipe_bom + dim_material | | 独有原料 | `compData[].unique_material_count` | 同上 | `unique_material_count` | `count(*) FILTER(ref_count=1)` | fact_recipe_bom(子查询ref_count) | | 高损耗原料 | `compData[].high_loss_count` | 同上 | `high_loss_count` | `count(*) FILTER(waste_rate > 20)` | fact_recipe_bom.waste_rate | | 复杂度 | `compData[].complexity_level` | 同上 | `complexity_level` | CASE:≤5=低, ≤10=正常, ≤15=较复杂, >15=重点评审 | — | #### 菜品物料构成(按SKU查询) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 物料 | `compDetail[].material_name` | /cost-analysis/bom-composition | `material_name` | — | dim_material.material_name | | 类型 | `compDetail[].material_type` | 同上 | `material_type` | `dim_material.major_category` | dim_material.major_category | | 标准毛用量 | `compDetail[].gross_qty` | 同上 | `gross_qty` | `standard_gross_quantity` | fact_recipe_bom.standard_gross_quantity | | 标准净用量 | `compDetail[].net_qty` | 同上 | `net_qty` | `standard_net_quantity` | fact_recipe_bom.standard_net_quantity | | 损耗率 | `compDetail[].waste_rate` | 同上 | `waste_rate` | — | fact_recipe_bom.waste_rate | | 成本占比 | `compDetail[].cost_share` | 同上 | `cost_share` | `theoretical_amount / sum(theoretical_amount) OVER(PARTITION BY summary_id) * 100` | dish_cost_analysis_material_detail | #### 高损耗BOM预警 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 菜品 | `lossData[].dish_name` | /cost-analysis/bom-high-loss | `dish_name` | `dim_sku.standard_name` | dim_sku | | 物料 | `lossData[].material_name` | 同上 | `material_name` | — | dim_material | | 损耗率 | `lossData[].waste_rate` | 同上 | `waste_rate` | — | fact_recipe_bom.waste_rate | | 出成率 | `lossData[].yield_rate` | 同上 | `yield_rate` | — | fact_recipe_bom.yield_rate | | 预警级别 | `lossData[].alert_level` | 同上 | `alert_level` | CASE:>100%=极端, >50%=严重, else=关注 | — | > 筛选条件:`waste_rate >= 20`。无月份参数。 #### 缺BOM的SKU清单(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | SKU编码 | `missData[].sku_code` | /cost-analysis/bom-missing | `sku_code` | — | dim_sku.sku_code | | 菜品名 | `missData[].dish_name` | 同上 | `dish_name` | `standard_name` | dim_sku.standard_name | | 品类 | `missData[].category_l1` | 同上 | `category_l1` | — | dim_sku.category_l1 | | 销售额 | `missData[].sales_amount` | 同上 | `sales_amount` | `COALESCE(dish_cost_analysis_summary.sales_amount, 0)` | dish_cost_analysis_summary | | 销量 | `missData[].sales_quantity` | 同上 | `sales_quantity` | `COALESCE(dish_cost_analysis_summary.sales_quantity, 0)` | 同上 | > 筛选条件:`sku_code NOT IN (SELECT DISTINCT sku_code FROM fact_recipe_bom)`。 --- ### Tab5:供应链与精简(SupplyTab) #### 原料共用度 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 物料 | `sharingData[].material_name` | /cost-analysis/material-sharing | `material_name` | — | dim_material.material_name | | 类型 | `sharingData[].major_category` | 同上 | `major_category` | — | dim_material.major_category | | 被引用SKU数 | `sharingData[].sku_count` | 同上 | `sku_count` | `count(DISTINCT sku_code)` | fact_recipe_bom.sku_code | | 分类 | `sharingData[].sharing_type` | 同上 | `sharing_type` | CASE:>50=核心原料, =1=独有原料, else=普通共用 | — | #### 独有原料风险(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | SKU编码 | `riskData[].sku_code` | /cost-analysis/unique-material-risk | `sku_code` | — | fact_recipe_bom | | 菜品名 | `riskData[].dish_name` | 同上 | `dish_name` | `dim_sku.standard_name` | dim_sku | | 品类 | `riskData[].category_l1` | 同上 | `category_l1` | — | dim_sku | | 销售额 | `riskData[].sales_amount` | 同上 | `sales_amount` | `COALESCE(dish_cost_analysis_summary.sales_amount, 0)` | dish_cost_analysis_summary | | 独有原料数 | `riskData[].unique_material_count` | 同上 | `unique_material_count` | `count(*) where ref_count=1` | fact_recipe_bom | | 风险等级 | `riskData[].risk_level` | 同上 | `risk_level` | CASE:sales<5000 & count>3=高, sales<5000=中, else=低 | — | #### SKU精简模拟器(POST) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 收入影响 | `simResult.summary.revenue_impact` | POST /cost-analysis/sku-simplify-simulate | `summary.revenue_impact` | `sum(sales_amount) for target SKUs` | dish_cost_analysis_summary | | 毛利影响 | `simResult.summary.profit_impact` | 同上 | `summary.profit_impact` | `sum(sales_amount - actual_cost)` | 同上 | | 可减少原料数 | `simResult.summary.releasable_materials` | 同上 | `summary.releasable_materials` | `count(DISTINCT material_code) 独有原料` | fact_recipe_bom | | 仍需采购的共用原料 | `simResult.summary.shared_materials` | 同上 | `summary.shared_materials` | `count(DISTINCT material_code) 共用原料` | 同上 | | 负毛利菜品数 | `simResult.summary.negative_margin_count` | 同上 | `summary.negative_margin_count` | `count(*) where actual_margin_rate_pct < 0` | dish_cost_analysis_summary | | 可释放物料 | `simResult.releasable_materials` | 同上 | `releasable_materials` | 独有物料明细 | fact_recipe_bom + dim_material | | 库存金额 | `releasable_materials[].inventory_value` | 同上 | `inventory_value` | `sum(actual_amount)` | dish_cost_analysis_material_detail | #### BOM驱动物料需求预测 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 物料 | `rawDemandData[].material_name` | /cost-analysis/material-demand | `material_name` | — | dim_material | | 类型 | `rawDemandData[].major_category` | 同上 | `major_category` | — | dim_material.major_category | | 单位 | `rawDemandData[].base_unit` | 同上 | `base_unit` | — | dim_material.base_unit | | 预计需求量 | `rawDemandData[].estimated_demand` | 同上 | `estimated_demand` | `sum(standard_gross_quantity * sales_quantity)` | fact_recipe_bom × dish_cost_analysis_summary | | 涉及菜品数 | `rawDemandData[].dish_count` | 同上 | `dish_count` | `count(DISTINCT sku_code)` | fact_recipe_bom | --- ### Tab6:包装耗材(PackagingTab) #### 包装总览(4个MetricCard) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 包装物料种类 | `ovData.packaging_types` | /cost-analysis/packaging-overview | `packaging_types` | `count(DISTINCT material_name)` | dish_cost_analysis_material_detail | | 包装总成本 | `ovData.total_cost` | 同上 | `total_cost` | `sum(theoretical_amount)` | 同上.theoretical_amount | | 总损耗量 | `ovData.total_loss_qty` | 同上 | `total_loss_qty` | `sum(loss_quantity)` | 同上.loss_quantity | | 高损耗包装数 | `ovData.high_loss_count` | 同上 | `high_loss_count` | `count(*) FILTER(loss_rate < -20%)` | 同上 | > 筛选条件:`material_name ~* '餐盒|餐具|打包|纸巾|碗|袋|杯|盒'`(正则匹配包装物料名称)。 #### 包装物料明细(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 物料 | `detailData[].material_name` | /cost-analysis/packaging-detail | `material_name` | — | dish_cost_analysis_material_detail | | 涉及菜品数 | `detailData[].dish_count` | 同上 | `dish_count` | `count(DISTINCT summary_id)` | 同上 | | 总用量 | `detailData[].total_qty` | 同上 | `total_qty` | `sum(theoretical_quantity)` | 同上 | | 总成本 | `detailData[].total_cost` | 同上 | `total_cost` | `sum(theoretical_amount)` | 同上 | | 损耗量 | `detailData[].loss_qty` | 同上 | `loss_qty` | `sum(loss_quantity)` | 同上 | | 平均损耗率 | `detailData[].avg_loss_rate` | 同上 | `avg_loss_rate` | `sum(loss_quantity)/sum(theoretical_quantity)*100` | 同上 | | 状态 | `detailData[].status` | 同上 | `status` | CASE:<-20%=核查, <-5%=关注, else=正常 | — | --- ### Tab7:数据质量(QualityTab) #### 数据质量总览(3个MetricCard + 6项检查) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | BOM覆盖率 | `ovData.bom_coverage` | /cost-analysis/data-quality | `bom_coverage` | `count(DISTINCT sku_code in BOM) / count(dim_sku) * 100` | fact_recipe_bom + dim_sku | | 菜品匹配率 | 前端计算 | 同上 | `matched_dishes / total_dishes` | `matched_dishes / total_dishes * 100` | dish_cost_analysis_summary + dish_sales_details | | 物料匹配率 | 前端计算 | 同上 | `matched_materials / total_materials` | `matched_materials / total_materials * 100` | dish_cost_analysis_material_detail + inventory_cost_records | | 零实际用量BOM | `ovData.zero_actual_bom` | 同上 | `zero_actual_bom` | `count(*) WHERE standard_net_quantity = 0` | fact_recipe_bom | | 负毛利率菜品 | `ovData.negative_margin_count` | 同上 | `negative_margin_count` | `count(*) WHERE actual_margin_rate_pct < 0` | dish_cost_analysis_summary | | 理论负毛利菜品 | `ovData.negative_theo_margin_count` | 同上 | `negative_theo_margin_count` | `count(*) WHERE theoretical_margin_rate_pct < 0` | 同上 | #### 未匹配菜品清单(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 菜品名 | `dishData[].dish_name` | /cost-analysis/unmatched-dishes | `dish_name` | dish_cost_analysis_summary | | 编码 | `dishData[].dish_code` | 同上 | `dish_code` | 同上 | | 品类 | `dishData[].category_level1` | 同上 | `category_level1` | 同上 | | 销售额 | `dishData[].sales_amount` | 同上 | `sales_amount` | 同上 | > 筛选条件:`NOT EXISTS (SELECT 1 FROM dish_sales_details WHERE dish_name = s.dish_name)`。 #### 未匹配物料清单(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 物料名 | `matData[].material_name` | /cost-analysis/unmatched-materials | `material_name` | dish_cost_analysis_material_detail | | 类型 | `matData[].material_type` | 同上 | `material_type` | 同上 | | 涉及菜品数 | `matData[].dish_count` | 同上 | `dish_count` | `count(DISTINCT summary_id)` | | 损耗金额 | `matData[].loss_amount` | 同上 | `loss_amount` | `sum(loss_amount)` | > 筛选条件:`NOT EXISTS (SELECT 1 FROM inventory_cost_records WHERE material_name = m.material_name)`。 --- ### Tab8:门店成本(StoreTab) #### 门店成本总览(6个MetricCard) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店总数 | `ovData.total_stores` | /cost-analysis/store-overview | `total_stores` | `count(*)` | mv_store_theoretical_actual_cost_monthly | | 红色-严重超耗 | `ovData.red_count` | 同上 | `red_count` | `count(*) FILTER(variance_level='红色-严重超耗')` | 同上.variance_level | | 橙色-明显超耗 | `ovData.orange_count` | 同上 | `orange_count` | `count(*) FILTER(variance_level='橙色-明显超耗')` | 同上 | | 绿色-基本正常 | `ovData.green_count` | 同上 | `green_count` | `count(*) FILTER(variance_level='绿色-基本正常')` | 同上 | | 灰色-口径异常 | `ovData.gray_count` | 同上 | `gray_count` | `count(*) FILTER(variance_level='灰色-口径异常')` | 同上 | | 总超耗金额 | `ovData.total_variance` | 同上 | `total_variance` | `sum(food_cost_variance)` | 同上.food_cost_variance | #### 门店超耗排名(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `rankData[].store_name` | /cost-analysis/store-ranking | `store_name` | — | mv_store_theoretical_actual_cost_monthly | | 理论成本率 | `rankData[].theo_cost_rate` | 同上 | `theo_cost_rate` | `theoretical_cost_rate_pct` | 同上 | | 实际成本率 | `rankData[].actual_cost_rate` | 同上 | `actual_cost_rate` | `actual_food_cost_rate_pct` | 同上 | | 偏差 | `rankData[].variance_pct` | 同上 | `variance_pct` | `variance_to_theoretical_pct` | 同上 | | 超耗金额 | `rankData[].variance_amount` | 同上 | `variance_amount` | `food_cost_variance` | 同上 | | 负库存项 | `rankData[].negative_item_lines` | 同上 | `negative_item_lines` | — | 同上.negative_item_lines | | 分级 | `rankData[].variance_level` | 同上 | `variance_level` | — | 同上.variance_level | --- ### Tab9:可视化探索(ExploreTab) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 菜品 | `scatterData[].dish_name` | /cost-analysis/scatter | `dish_name` | — | dish_cost_analysis_summary | | 品类 | `scatterData[].category_level1` | 同上 | `category_level1` | — | 同上 | | 销量(X轴) | `scatterData[].x` | 同上 | `x` | `sales_quantity` | 同上 | | 成本差异(Y轴) | `scatterData[].y` | 同上 | `y` | `cost_variance_amount` | 同上 | | 销售额(气泡大小) | `scatterData[].size` | 同上 | `size` | `sales_amount` | 同上 | --- ### Tab10:调整管理(AdjustmentTab) #### 子Tab:待处理建议 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 菜品 | `diagData[].dish_name` | /cost-analysis/diagnosis | `dish_name` | — | dish_diagnosis_snapshot | | 品类 | `diagData[].category_l1` | 同上 | `category_l1` | — | 同上 | | 诊断类型 | `diagData[].diagnosis_type` | 同上 | `diagnosis_type` | — | 同上(生成时计算) | | 诊断详情 | `diagData[].diagnosis_detail` | 同上 | `diagnosis_detail` | — | 同上 | | 理论毛利率 | `diagData[].theoretical_margin_pct` | 同上 | `theoretical_margin_pct` | — | 同上 | | 实际毛利率 | `diagData[].actual_margin_pct` | 同上 | `actual_margin_pct` | — | 同上 | | 成本差异 | `diagData[].cost_variance_amount` | 同上 | `cost_variance_amount` | — | 同上 | | 建议动作 | `diagData[].suggested_action` | 同上 | `suggested_action` | — | 同上 | | 优先级 | `diagData[].priority` | 同上 | `priority` | P0/P1/P2/P3 | 同上 | > **诊断生成**:POST `/cost-analysis/diagnosis/generate` 从 `dish_cost_analysis_summary` + BOM数据生成快照写入 `dish_diagnosis_snapshot`。 #### 子Tab:调整记录 | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 菜品 | `adjData[].dish_name` | /cost-analysis/adjustment | `dish_name` | dish_adjustment_log | | 调整类型 | `adjData[].adjustment_type` | 同上 | `adjustment_type` | 同上 | | 执行日期 | `adjData[].effective_date` | 同上 | `effective_date` | 同上 | | 调整前毛利率 | `adjData[].before_theoretical_margin` | 同上 | `before_theoretical_margin` | 同上 | | 目标毛利率 | `adjData[].after_target_margin` | 同上 | `after_target_margin` | 同上 | | 决策人 | `adjData[].decided_by` | 同上 | `decided_by` | 同上 | | 状态 | `adjData[].status` | 同上 | `status` | 同上 | > **创建调整**:POST `/cost-analysis/adjustment` → INSERT INTO dish_adjustment_log。 > **更新状态**:PUT `/cost-analysis/adjustment/:id` → UPDATE status。 #### 子Tab:效果验证 | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 调整前日均销量 | `verifyData.before_avg_daily` | /cost-analysis/adjustment/:id/verify | `before_avg_daily` | `avg(bills) 前7天` | dish_sales_details | | 调整后日均销量 | `verifyData.after_avg_daily` | 同上 | `after_avg_daily` | `avg(bills) 后7天` | 同上 | | 销量变化率 | `verifyData.sales_change_pct` | 同上 | `sales_change_pct` | `(afterAvg - beforeAvg) / beforeAvg * 100` | — | | 日销量趋势 | `verifyData.before/after` | 同上 | `before/after` | `count(*) per day` | dish_sales_details | --- ## 七、页面5:门店营业费用 StoreExpensePage > 10个Tab,每个Tab独立组件。共调用11个API(全部GET)。 > 核心数据源:`analytics.mv_store_operating_expense_monthly`(门店营业费用月度物化视图)+ `analytics.mv_store_risk_rating_monthly`(门店风险评级月度物化视图)+ `analytics.v_operating_expense_account_monthly`(费用科目月度视图)。 ### Tab1:费用总览(ExpenseOverviewTab) #### 概览指标(6个MetricCard) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 营业收入 | `ov.total_consumption` | /store-expense/overview | `total_consumption` | `sum(consumption)` | bill_fact.consumption + mv_store_risk_rating_monthly | | 优惠 | `ov.total_discount` | 同上 | `total_discount` | `sum(discount)` | 同上 | | 实收合计 | `ov.total_received` | 同上 | `total_received` | `sum(received)` | mv_store_risk_rating_monthly.received | | 门店贡献利润 | `ov.total_contribution` | 同上 | `total_contribution` | `sum(received - actual_food_cost - operating_expense)` | mv_store_operating_expense_monthly | | 经营门店 | `ov.total_stores` | 同上 | `total_stores` | `count(*)` | mv_store_risk_rating_monthly | | 费用率 | `ov.overall_expense_rate_pct` | 同上 | `overall_expense_rate_pct` | `sum(operating_expense) / sum(received) * 100` | 同上 | > **数据关联机制**:overview API通过CTE `full_scope` 关联 `mv_store_risk_rating_monthly`(主表,按month_start过滤)LEFT JOIN `mv_store_operating_expense_monthly`(费用明细,按report_month过滤)LEFT JOIN `bill_fact`(消费/优惠金额)。 #### 费用结构分析(PieChart + 明细表) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 费用科目 | `structureData[].account_name` | /store-expense/expense-structure | `account_name` | — | v_operating_expense_account_monthly.account_name | | 金额 | `structureData[].amount` | 同上 | `amount` | — | 同上.amount | | 占比 | `structureData[].expense_share_pct` | 同上 | `expense_share_pct` | — | 同上.expense_share_pct | | 门店数 | `structureData[].nonzero_cost_unit_count` | 同上 | `nonzero_cost_unit_count` | — | 同上.nonzero_cost_unit_count | > ⚠️ **CollapsibleSection subtitle 硬编码日期**:`subtitle="2026年4月各费用科目占比"` 硬编码了月份,应使用 `month` 变量动态显示。 --- ### Tab2:门店费用率排名(StoreRankingTab) #### 门店费用率排名(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `rows[].sales_store_name` | /store-expense/store-ranking | `sales_store_name` | — | mv_store_operating_expense_monthly | | 实收 | `rows[].received` | 同上 | `received` | — | 同上.received | | 费用合计 | `rows[].operating_expense` | 同上 | `operating_expense` | — | 同上.operating_expense | | 费用率 | `rows[].operating_expense_rate_pct` | 同上 | `operating_expense_rate_pct` | — | 同上 | | 人工率 | `rows[].wage_rate_pct` | 同上 | `wage_rate_pct` | — | 同上 | | 租金率 | `rows[].rent_rate_pct` | 同上 | `rent_rate_pct` | — | 同上 | | 水电率 | `rows[].utility_rate_pct` | 同上 | `utility_rate_pct` | — | 同上 | | 贡献利润 | `rows[].actual_store_contribution` | 同上 | `actual_store_contribution` | — | 同上 | | 贡献率 | `rows[].actual_store_contribution_rate_pct` | 同上 | `actual_store_contribution_rate_pct` | — | 同上 | | 坪效 | `rows[].received_per_sqm` | 同上 | `received_per_sqm` | — | 同上 | > 筛选条件:filter支持 profitable/loss/zero_sales。排序白名单校验。 --- ### Tab3:门店贡献利润(ContributionTab) #### 门店贡献利润分析(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `rows[].sales_store_name` | /store-expense/store-contribution | `sales_store_name` | — | mv_store_operating_expense_monthly | | 实收 | `rows[].received` | 同上 | `received` | — | 同上 | | 理论食材成本 | `rows[].theoretical_cost` | 同上 | `theoretical_cost` | — | 同上.theoretical_cost | | 实际食材成本 | `rows[].actual_food_cost` | 同上 | `actual_food_cost` | — | 同上.actual_food_cost | | 营业费用 | `rows[].operating_expense` | 同上 | `operating_expense` | — | 同上 | | 理论贡献 | `rows[].theoretical_store_contribution` | 同上 | `theoretical_store_contribution` | — | 同上 | | 理论贡献率 | `rows[].theoretical_store_contribution_rate_pct` | 同上 | `theoretical_store_contribution_rate_pct` | — | 同上 | | 实际贡献 | `rows[].actual_store_contribution` | 同上 | `actual_store_contribution` | `received - actual_food_cost - operating_expense` | 同上 | | 实际贡献率 | `rows[].actual_store_contribution_rate_pct` | 同上 | `actual_store_contribution_rate_pct` | — | 同上 | | 贡献差异 | `rows[].contribution_variance` | 同上 | `contribution_variance` | `actual_store_contribution - theoretical_store_contribution` | 同上 | > ⚠️ **ContributionTab 默认排序方向问题**:前端 `order` 初始值为 `'asc'`(升序),但后端 `/store-contribution` 的 `order` 参数处理为 `=== 'desc' ? 'DESC' : 'ASC'`,即非'desc'均为'ASC'。这意味着默认按贡献率升序(最差门店在前),这是合理的行为。但前端 `defaultOrder="asc"` 与 `ContributionTab` 的 `useState<'asc' | 'desc'>('asc')` 一致,无问题。 --- ### Tab4:房租及租约风险(RentRiskTab) #### 房租及租约风险(服务端分页 + riskOnly切换) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `rows[].sales_store_name` | /store-expense/rent-risk | `sales_store_name` | — | mv_store_operating_expense_monthly | | 实收 | `rows[].received` | 同上 | `received` | — | 同上 | | 租金 | `rows[].rent_expense` | 同上 | `rent_expense` | — | 同上 | | 租金率 | `rows[].rent_rate_pct` | 同上 | `rent_rate_pct` | — | 同上 | | 面积(㎡) | `rows[].area_sqm` | 同上 | `area_sqm` | — | 同上 | | 坪效 | `rows[].received_per_sqm` | 同上 | `received_per_sqm` | — | 同上 | | 租约到期 | `rows[].lease_expiry_date` | 同上 | `lease_expiry_date` | — | 同上.lease_expiry_date | | 风险等级 | `rows[].risk_level` | 同上 | `risk_level` | CASE:≤3月=即将到期, ≤12月=年内到期, ≤18月=明年上半年到期, rent>20%=租金占比偏高, else=正常 | — | | 建议 | `rows[].suggestion` | 同上 | `suggestion` | CASE:按到期/租金率生成建议文本 | — | > **riskOnly 筛选**:`lease_expiry_date <= month + 12 months OR rent_rate_pct > 20`。 > ⚠️ **租约到期日期显示**:前端使用 `new Date(r.lease_expiry_date).toLocaleDateString('zh-CN')`,后端返回的是日期类型,可能包含时间部分。根据规范应使用 `substring(0, 10)` 格式化。 --- ### Tab5:外卖佣金分析(DeliveryTab) #### 外卖佣金分析(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `rows[].sales_store_name` | /store-expense/delivery-commission | `sales_store_name` | — | mv_store_operating_expense_monthly | | 实收 | `rows[].received` | 同上 | `received` | — | 同上 | | 外卖收入 | `rows[].delivery_received` | 同上 | `delivery_received` | — | 同上 | | 外卖占比 | `rows[].delivery_sales_share_pct` | 同上 | `delivery_sales_share_pct` | — | 同上 | | 佣金支出 | `rows[].delivery_commission_expense` | 同上 | `delivery_commission_expense` | — | 同上 | | 佣金率 | `rows[].commission_to_delivery_sales_pct` | 同上 | `commission_to_delivery_sales_pct` | — | 同上 | | 综合平台成本率 | `rows[].combined_platform_cost_rate_pct` | 同上 | `combined_platform_cost_rate_pct` | — | 同上 | | 外卖类型 | `rows[].delivery_type` | 同上 | `delivery_type` | CASE:>40%=重度依赖, >25%=外卖占比较高, >10%=正常, else=以堂食为主 | — | | 建议 | `rows[].suggestion` | 同上 | `suggestion` | CASE:按佣金率/外卖占比/综合成本率生成建议 | — | > 筛选条件:`delivery_received > 0`。 --- ### Tab6:人效坪效(EfficiencyTab) #### 人效坪效分析(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `rows[].sales_store_name` | /store-expense/efficiency | `sales_store_name` | — | mv_store_operating_expense_monthly | | 实收 | `rows[].received` | 同上 | `received` | — | 同上 | | 面积(㎡) | `rows[].area_sqm` | 同上 | `area_sqm` | — | 同上 | | 坪效 | `rows[].received_per_sqm` | 同上 | `received_per_sqm` | — | 同上 | | 人工成本 | `rows[].wage_expense` | 同上 | `wage_expense` | — | 同上 | | 人工成本率 | `rows[].wage_rate_pct` | 同上 | `wage_rate_pct` | — | 同上 | | 人效(元/元人工) | `rows[].revenue_per_wage` | 同上 | `revenue_per_wage` | `received / wage_expense` | 同上 | | 账单数 | `rows[].bill_count` | 同上 | `bill_count` | — | 同上 | | 客单价 | `rows[].avg_ticket_size` | 同上 | `avg_ticket_size` | `received / bill_count` | 同上 | | 效率等级 | `rows[].efficiency_level` | 同上 | `efficiency_level` | CASE:<1000=坪效偏低, <2000=坪效一般, <4000=坪效良好, else=坪效优秀 | — | | 建议 | `rows[].suggestion` | 同上 | `suggestion` | CASE:按人工率/坪效/面积生成建议 | — | > 筛选条件:`received > 0 AND area_sqm IS NOT NULL`。 > ⚠️ **效率等级缺少"坪效极低"档位**:后端 `efficiency_level` CASE 只有4档(偏低/一般/良好/优秀),但前端 `EfficiencyTab` 的颜色判断有5档(极低<500/偏低<1000/一般/良好/优秀),前端 `efficiency_level === '坪效极低'` 永远不会匹配。后端 loss-diagnosis 中的 `efficiency_status` 才有"坪效极低"档(<500)。 --- ### Tab7:固定/变动费用(FixedVariableTab) #### 固定/变动费用拆分(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `rows[].sales_store_name` | /store-expense/fixed-variable | `sales_store_name` | — | mv_store_operating_expense_monthly | | 实收 | `rows[].received` | 同上 | `received` | — | 同上 | | 固定费用 | `rows[].fixed_expense` | 同上 | `fixed_expense` | `rent_expense + dorm_expense + repair_clean_expense * 0.5` | 同上 | | 固定费用率 | `rows[].fixed_rate_pct` | 同上 | `fixed_rate_pct` | `fixed_expense / received * 100` | 同上 | | 变动费用 | `rows[].variable_expense` | 同上 | `variable_expense` | `wage_expense + utility_expense + delivery_commission_expense + card_fee_expense + repair_clean_expense * 0.5` | 同上 | | 变动费用率 | `rows[].variable_rate_pct` | 同上 | `variable_rate_pct` | `variable_expense / received * 100` | 同上 | | 边际贡献(扣变动) | `rows[].contribution_after_variable` | 同上 | `contribution_after_variable` | `received - actual_food_cost - wage_expense - utility_expense - delivery_commission_expense - card_fee_expense - repair_clean_expense * 0.5` | 同上 | | 盈亏平衡销售额 | `rows[].break_even_sales` | 同上 | `break_even_sales` | `fixed_expense`(即 rent_expense + dorm_expense + repair_clean_expense * 0.5) | 同上 | > ⚠️ **盈亏平衡销售额计算错误**:后端 `break_even_sales` 直接等于 `fixed_expense`(固定费用),但正确的盈亏平衡公式应为 `固定费用 ÷ (1 - 食材成本率)` 或 `固定费用 ÷ 边际贡献率`。当前计算仅返回固定费用金额本身,并非真正的盈亏平衡点。Tab8 `break-even` 中的计算才是正确的。 --- ### Tab8:盈亏平衡分析(BreakEvenTab) #### 盈亏平衡分析(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `rows[].sales_store_name` | /store-expense/break-even | `sales_store_name` | — | mv_store_operating_expense_monthly | | 实收 | `rows[].received` | 同上 | `received` | — | 同上 | | 食材成本 | `rows[].actual_food_cost` | 同上 | `actual_food_cost` | — | 同上 | | 食材成本率 | `rows[].food_cost_rate_pct` | 同上 | `food_cost_rate_pct` | `actual_food_cost / received * 100` | 同上 | | 营业费用 | `rows[].operating_expense` | 同上 | `operating_expense` | — | 同上 | | 费用率 | `rows[].expense_rate_pct` | 同上 | `expense_rate_pct` | `operating_expense / received * 100` | 同上 | | 盈亏平衡销售额 | `rows[].break_even_sales` | 同上 | `break_even_sales` | `operating_expense / (1 - actual_food_cost/received)` | 同上 | | 销售缺口 | `rows[].sales_gap` | 同上 | `sales_gap` | `received - break_even_sales` | 同上 | | 安全边际率 | `rows[].safety_margin_pct` | 同上 | `safety_margin_pct` | `(received - break_even_sales) / break_even_sales * 100` | 同上 | | 安全状态 | `rows[].safety_status` | 同上 | `safety_status` | CASE:贡献边际≤0=无法盈利, received<平衡点=未达盈亏平衡, <15%=接近, <30%=较低, else=充足 | — | > **正确的盈亏平衡公式**:`break_even_sales = operating_expense / (1 - food_cost_ratio)`,即 `营业费用 ÷ (1 - 食材成本率)`。与Tab7的简单固定费用公式不同。 --- ### Tab9:亏损门店诊断(LossDiagnosisTab) #### 概览指标(4个MetricCard) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 亏损门店数 | `meta.total` | /store-expense/loss-diagnosis | `meta.total` | `count(*) WHERE actual_store_contribution <= 0 AND received > 0` | mv_store_operating_expense_monthly | | P0 紧急 | `p0Count` | 同上 | 前端计算 | `rows.filter(priority === 'P0').length` | — | | P1 整改 | `p1Count` | 同上 | 前端计算 | `rows.filter(priority === 'P1').length` | — | | 亏损金额合计 | `totalLoss` | 同上 | 前端计算 | `rows.reduce(sum + actual_store_contribution)` | — | > ⚠️ **P0/P1/亏损金额仅统计当前页**:`p0Count`、`p1Count`、`totalLoss` 是从当前页 `rows` 计算的,而非全量数据。当翻页时这些指标会变化,不具备全局统计意义。应在后端返回全量统计或单独查询。 #### 亏损门店诊断明细(服务端分页 + 弹窗详情) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `rows[].sales_store_name` | /store-expense/loss-diagnosis | `sales_store_name` | — | mv_store_operating_expense_monthly | | 优先级 | `rows[].priority` | 同上 | 后端计算 | CASE:贡献率<-20%=P0, <-5%=P1, <0=P2, else=P3 | — | | 亏损类型 | `rows[].loss_type` | 同上 | `loss_type` | CASE:理论贡献≤0=理论即亏损, 实际超耗=实际超耗导致亏损, else=费用过高导致亏损 | — | | 实收 | `rows[].received` | 同上 | `received` | — | 同上 | | 贡献利润 | `rows[].actual_store_contribution` | 同上 | `actual_store_contribution` | — | 同上 | | 贡献率 | `rows[].actual_store_contribution_rate_pct` | 同上 | `actual_store_contribution_rate_pct` | — | 同上 | | 人工率 | `rows[].wage_rate_pct` | 同上 | `wage_rate_pct` | — | 同上 | | 租金率 | `rows[].rent_rate_pct` | 同上 | `rent_rate_pct` | — | 同上 | | 坪效 | `rows[].received_per_sqm` | 同上 | `received_per_sqm` | — | 同上 | | 亏损原因 | `rows[].diagnosis_reasons` | 同上 | 后端JS生成 | 按收入/食材/人工/租金/水电/外卖/坪效/租约/亏损类型多维度拼接 | — | | 建议动作 | `rows[].suggested_actions` | 同上 | 后端JS生成 | 对应改进建议动作列表 | — | > **诊断状态字段**(弹窗详情):`revenue_status`、`food_cost_status`、`wage_status`、`rent_status`、`utility_status`、`delivery_status`、`efficiency_status`、`lease_status` 均由后端SQL CASE生成。 > ⚠️ **租约到期日期显示**:弹窗中使用 `new Date(store.lease_expiry_date).toLocaleDateString('zh-CN')`,应使用 `substring(0, 10)` 规范格式。 --- ### Tab10:关停/续租/改造评估(StoreEvaluationTab) #### 概览指标(4个MetricCard) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 关停评估 | `evalStats['关停评估']` | /store-expense/store-evaluation | `meta.evalStats.关停评估` | 后端全量统计 | mv_store_operating_expense_monthly | | 改造评估 | `evalStats['改造评估']` | 同上 | `meta.evalStats.改造评估` | 后端全量统计 | 同上 | | 续租评估 | `evalStats['续租评估']` | 同上 | `meta.evalStats.续租评估` | 后端全量统计 | 同上 | | 关注观察 | `evalStats['关注观察']` | 同上 | `meta.evalStats.关注观察` | 后端全量统计 | 同上 | > **evalStats 全量统计**:后端在返回分页数据的同时,额外执行一条全量统计SQL获取各评估类型数量,不受分页影响。与Tab9的仅当前页统计形成对比。 #### 门店评估明细(服务端分页 + 弹窗详情) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `rows[].sales_store_name` | /store-expense/store-evaluation | `sales_store_name` | — | mv_store_operating_expense_monthly | | 优先级 | `rows[].priority` | 同上 | `priority` | CASE:received<5000=P0, 贡献率<-20%=P0, <-10%=P1, <0=P2, <10=P3, else=P4 | — | | 评估类型 | `rows[].evaluation_type` | 同上 | `evaluation_type` | CASE:received<5000=关停评估, 贡献率<-20%&租约到期=关停评估, 贡献率<-10%&坪效<1000=关停评估, 贡献率<0&租约到期=关停或迁址评估, 贡献率<0&坪效<1000&面积>400=改造评估, 贡献率<0&人工率>35=改造评估, 贡献率<0=关注观察, 租约≤3月=续租评估, 贡献率<10=关注观察, else=正常经营 | — | | 实收 | `rows[].received` | 同上 | `received` | — | 同上 | | 贡献利润 | `rows[].actual_store_contribution` | 同上 | `actual_store_contribution` | — | 同上 | | 贡献率 | `rows[].actual_store_contribution_rate_pct` | 同上 | `actual_store_contribution_rate_pct` | — | 同上 | | 坪效 | `rows[].received_per_sqm` | 同上 | `received_per_sqm` | — | 同上 | | 租约到期 | `rows[].lease_expiry_date` | 同上 | `lease_expiry_date` | 后端格式化为 `toISOString().split('T')[0]` | 同上 | | 评估详情 | `rows[].evaluation_detail` | 同上 | 后端JS生成 | 按评估类型生成详细说明 | — | --- ## 八、页面6:风险与内控 RiskPage > 单页面无子组件文件,4个Tab切换(异常账单/全额优惠/空单/收银员风险),3个API(全部GET)。 > 核心数据源:`mv_risk_anomaly`(异常账单物化视图)、`mv_risk_zero`(零实收账单物化视图)、`mv_risk_cashier`(收银员风险物化视图)。均按 `month = to_char($1::date, 'YYYY-MM')` 过滤。 ### 概览指标(5个MetricCard) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 异常账单数 | `totalAnomaly` | /risk/anomaly | `meta.total` | `count(*)` | mv_risk_anomaly | | 全额优惠账单 | `zeroMeta.full_discount` | /risk/zero-received | `meta.full_discount` | `count(*) FILTER (zero_received_type='全额优惠')` | mv_risk_zero | | 空单账单 | `zeroMeta.empty_bill` | 同上 | `meta.empty_bill` | `count(*) FILTER (zero_received_type='无消费无优惠')` | 同上 | | 高风险收银员(≥10%) | `highRiskCashiers` | /risk/cashier | 前端计算 | `cashierRows.filter(anomaly_rate_pct >= 10).length` | mv_risk_cashier | | 平均异常率 | `avgAnomalyRate` | 同上 | 前端计算 | `sum(anomaly_rate_pct) / cashierRows.length` | 同上 | > ⚠️ **highRiskCashiers 和 avgAnomalyRate 基于全量收银员数据计算**:`/risk/cashier` 不分页,返回全量收银员列表,所以这两个指标是全局准确的。与StoreExpensePage Tab9的当前页统计问题不同。 ### 收银员异常账单数TOP15(BarChart) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 收银员 | `cashierChartData[].cashier` | /risk/cashier | `cashier` | — | mv_risk_cashier | | 异常账单数 | `cashierChartData[].anomaly_bills` | 同上 | `anomaly_bills` | `anomaly_no_received + anomaly_discount_over + anomaly_unbalanced` | 同上 | > 图表数据:取 `cashierRows` 按 `anomaly_bills` 降序排列前15条。 ### Tab1:异常账单(anomaly) #### 异常账单明细(服务端分页 + 多维筛选) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `anomalyRows[].store_name` | /risk/anomaly | `store_name` | — | mv_risk_anomaly | | 账单号 | `anomalyRows[].bill_no` | 同上 | `bill_no` | — | 同上 | | 餐段 | `anomalyRows[].meal_period` | 同上 | `meal_period` | — | 同上 | | 消费额 | `anomalyRows[].consumption` | 同上 | `consumption` | — | 同上 | | 优惠 | `anomalyRows[].discount_total` | 同上 | `discount_total` | — | 同上 | | 实收 | `anomalyRows[].received_total` | 同上 | `received_total` | — | 同上 | | 异常原因 | `anomalyRows[].anomaly_reason` | 同上 | `anomaly_reason` | — | 同上 | | 收银员 | `anomalyRows[].cashier` | 同上 | `cashier` | — | 同上 | | 结账时间 | `anomalyRows[].closed_at` | 同上 | `closed_at` | — | 同上 | > **筛选条件**:store(门店精确匹配)、reason(异常原因精确匹配)、cashier(ILIKE模糊搜索,前端400ms debounce)。 > **排序白名单**:`consumption`、`discount_total`、`received_total`、`closed_at`。 > **筛选合计**:后端在count查询中同时返回 `sum_consumption`、`sum_discount`、`sum_received`,前端显示筛选合计行。 > **结账时间格式化**:前端使用 `r.closed_at?.substring(0, 16)` 截取前16位,符合日期格式化规范。 ### Tab2:全额优惠(full_discount) #### 全额优惠账单(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `zeroRows[].store_name` | /risk/zero-received | `store_name` | — | mv_risk_zero | | 账单号 | `zeroRows[].bill_no` | 同上 | `bill_no` | — | 同上 | | 餐段 | `zeroRows[].meal_period` | 同上 | `meal_period` | — | 同上 | | 消费额 | `zeroRows[].consumption` | 同上 | `consumption` | — | 同上 | | 优惠 | `zeroRows[].discount_total` | 同上 | `discount_total` | — | 同上 | | 收银员 | `zeroRows[].cashier` | 同上 | `cashier` | — | 同上 | | 结账时间 | `zeroRows[].closed_at` | 同上 | `closed_at` | — | 同上 | > API请求参数:`type='全额优惠'`,后端按 `zero_received_type = '全额优惠'` 过滤。 > 排序:后端固定 `ORDER BY consumption DESC`,前端 `sortOptions` 不生效(未传sort参数到后端)。 ### Tab3:空单(empty_bill) #### 空单账单(服务端分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 同Tab2 | 同上 | /risk/zero-received | 同上 | 同上 | mv_risk_zero | > API请求参数:`type='无消费无优惠'`,后端按 `zero_received_type = '无消费无优惠'` 过滤。 > ⚠️ **Tab2和Tab3共用同一API和组件结构**:`zeroType` 切换时触发重新请求,`zeroPage` 重置为1。 ### Tab4:收银员风险(cashier) #### 收银员风险明细(全量不分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 收银员 | `cashierRows[].cashier` | /risk/cashier | `cashier` | — | mv_risk_cashier | | 账单数 | `cashierRows[].bill_count` | 同上 | `bill_count` | — | 同上 | | 实收 | `cashierRows[].received` | 同上 | `received` | — | 同上 | | 异常账单数 | `cashierRows[].anomaly_bills` | 同上 | `anomaly_bills` | `anomaly_no_received + anomaly_discount_over + anomaly_unbalanced` | 同上 | | 异常率 | `cashierRows[].anomaly_rate_pct` | 同上 | `anomaly_rate_pct` | `(anomaly_bills / bill_count) * 100` | 同上 | | 异常消费额 | `cashierRows[].anomaly_consumption` | 同上 | `consumption_total` | — | 同上 | > ⚠️ **前端字段名与后端不匹配**:前端列 `anomaly_consumption` 对应后端返回的 `consumption_total`,前端 `render: (r) => formatCurrency(r.anomaly_consumption)` 访问的 `r.anomaly_consumption` 在后端返回中不存在(后端返回 `consumption_total`),导致该列始终显示 ¥0.00。 > **双击行跳转**:`onRowDoubleClick` 设置 `anomalyCashier` 并切换到 anomaly Tab,用收银员名筛选异常账单。 > **排序**:前端 `sortOptions` 有5个选项,但后端固定 `ORDER BY anomaly_bills DESC`,前端排序不生效(未传sort参数到后端)。 --- ## 九、页面7:成本与库存 CostPage > 单页面无子组件文件,4个区块(成本概览+散点图+成本对比表+库存效率+原料分类基准),3个API(全部GET)。 > 核心数据源:`analytics.mv_store_theoretical_actual_cost_monthly`(理论vs实际成本月度物化视图)、`analytics.mv_inventory_cost_classified_monthly`(库存分类成本月度物化视图)、`analytics.mv_store_area_efficiency_monthly`(门店面积效率月度物化视图)。均按 `month_start = $1` 过滤。 ### 区块1:成本概览(4个MetricCard) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 平均理论成本率 | `avgTheoreticalRate` | /cost/comparison | 前端计算 | `avg(theoretical_cost_rate_pct)` 排除口径异常 | mv_store_theoretical_actual_cost_monthly | | 平均实际成本率 | `avgActualRate` | 同上 | 前端计算 | `avg(actual_food_cost_rate_pct)` 排除口径异常 | 同上 | | 高偏差门店(>20%) | `highVarianceCount` | 同上 | 前端计算 | `filter(variance_to_theoretical_pct > 20).length` 排除口径异常 | 同上 | | 口径异常门店 | `abnormalRows.length` | 同上 | 前端计算 | `filter(variance_level includes '口径异常').length` | 同上 | > **口径异常处理**:前端将 `variance_level` 包含"口径异常"的门店分离为 `abnormalRows`,不纳入平均成本率和高偏差统计。显示异常门店名称列表。 > **全量数据**:`/cost/comparison` 不分页,返回全量门店数据,前端统计是全局准确的。 ### 区块2:理论vs实际成本率散点图(ScatterChart) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店名 | `scatterData[].name` | /cost/comparison | `store_name` | — | mv_store_theoretical_actual_cost_monthly | | 理论成本率 | `scatterData[].theoretical` | 同上 | `theoretical_cost_rate_pct` | — | 同上 | | 实际成本率 | `scatterData[].actual` | 同上 | `actual_food_cost_rate_pct` | — | 同上 | | 偏差 | `scatterData[].variance` | 同上 | `variance_to_theoretical_pct` | — | 同上 | > 散点图排除口径异常门店。X轴=理论成本率,Y轴=实际成本率,对角线参考线表示理论=实际。偏离对角线越多偏差越大。 > **动态坐标轴**:`useMemo` 计算数据范围 ±1 作为domain,空数据时默认 [0,40]×[0,50]。 ### 区块3:门店成本对比表(FilterableTable,全量不分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `costRows[].store_name` | /cost/comparison | `store_name` | — | mv_store_theoretical_actual_cost_monthly | | 理论成本率 | `costRows[].theoretical_cost_rate_pct` | 同上 | `theoretical_cost_rate_pct` | — | 同上 | | 实际成本率 | `costRows[].actual_food_cost_rate_pct` | 同上 | `actual_food_cost_rate_pct` | — | 同上 | | 偏差 | `costRows[].variance_to_theoretical_pct` | 同上 | `variance_to_theoretical_pct` | — | 同上 | | 偏差等级 | `costRows[].variance_level` | 同上 | `variance_level` | — | 同上 | | 实际食材成本 | `costRows[].actual_food_cost` | 同上 | `actual_food_cost` | — | 同上 | > **筛选**:`filterKey="variance_level"` 按偏差等级筛选。**排序**:前端 `sortOptions` 有5个选项,但后端固定 `ORDER BY variance_to_theoretical_pct DESC`,前端排序为客户端排序(全量数据)。 > ⚠️ **前端排序 vs 后端排序**:`/cost/comparison` 不分页返回全量数据,`FilterableTable` 未设 `serverSide`,排序在前端完成,是有效的。 ### 区块4:库存效率(FilterableTable,懒加载) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `invRows[].store_name` | /cost/inventory | `store_name` | — | mv_store_area_efficiency_monthly + mv_store_theoretical_actual_cost_monthly | | 业态 | `invRows[].business_type` | 同上 | `business_type` | — | mv_store_area_efficiency_monthly | | 规模 | `invRows[].scale_tier` | 同上 | `scale_tier` | — | 同上 | | 库存天数 | `invRows[].estimated_inventory_days` | 同上 | `estimated_inventory_days` | — | 同上 | | 期末库存 | `invRows[].ending_inventory_amount` | 同上 | `ending_inventory_amount` | — | mv_store_theoretical_actual_cost_monthly | | 负耗用行数 | `invRows[].negative_item_lines` | 同上 | `negative_item_lines` | — | 同上 | > **懒加载**:`enabled: invOpen`,仅在CollapsibleSection展开时请求。`onToggle={setInvOpen}` 控制展开状态。 > **数据关联**:`mv_store_area_efficiency_monthly` LEFT JOIN `mv_store_theoretical_actual_cost_monthly` ON store_code + month_start。 ### 区块5:原料分类成本基准(FilterableTable,懒加载) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `catRows[].store_name` | /cost/category-benchmark | `store_name` | — | mv_inventory_cost_classified_monthly | | 财务分类 | `catRows[].finance_category` | 同上 | `finance_category` | — | 同上 | | 实际成本 | `catRows[].actual_category_cost` | 同上 | `actual_category_cost` | `sum(consumption_amount) GROUP BY store_name, finance_category` | 同上 | | 每万元成本 | `catRows[].cost_per_10k_sales` | 同上 | `cost_per_10k_sales` | `actual_category_cost / sales_received * 10000` | 同上 + mv_store_theoretical_actual_cost_monthly | | 同业中位 | `catRows[].peer_median_cost_per_10k` | 同上 | `peer_median_cost_per_10k` | `percentile_cont(0.5) WITHIN GROUP (ORDER BY cost_per_10k_sales)` 按分类 | — | | 超出同业 | `catRows[].excess_vs_peer_per_10k` | 同上 | `excess_vs_peer_per_10k` | `cost_per_10k_sales - peer_median` | — | > **懒加载**:`enabled: catOpen`。**同业基准**:使用 `percentile_cont(0.5)` 计算各财务分类的每万元成本中位数作为同业基准。 --- ## 十、页面8:会员与复购 MemberPage > 单页面无子组件文件,4个区块(会员概览+对比图+对比明细+复购率),2个API(全部GET)。 > 核心数据源:`analytics.bill_fact`(账单事实表,按closed_at月份过滤)、`analytics.v_store_repeat_summary_monthly`(门店复购汇总月度视图,按month_start过滤)。 ### 区块1:会员概览(4个MetricCard) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 会员账单占比 | `memberBillShare` | /member/comparison | 前端计算 | `memberRow.bill_count / (memberRow.bill_count + nonMemberRow.bill_count) * 100` | bill_fact | | 会员收入占比 | `memberRevenueShare` | 同上 | 前端计算 | `memberRow.received / (memberRow.received + nonMemberRow.received) * 100` | 同上 | | 平均复购率 | `avgRepeatRate` | /member/repeat | 前端计算 | `avg(repeat_rate_pct)` | v_store_repeat_summary_monthly | | 低复购门店(<30%) | `lowRepeatCount` | 同上 | 前端计算 | `filter(repeat_rate_pct < 30).length` | 同上 | > **全量数据**:两个API均不分页,返回全量数据,前端统计全局准确。 ### 区块2:会员vs非会员消费对比(BarChart + PieChart) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 客户类型 | `cmpChartData[].name` | /member/comparison | `customer_type` | `CASE WHEN member_id IS NULL THEN '非会员' ELSE '会员'` | bill_fact | | 账单数 | `cmpChartData[].账单数` | 同上 | `bill_count` | `count(*)` | 同上 | | 实收 | `cmpChartData[].实收` | 同上 | `received` | `sum(received_total)` | 同上 | | 客单价 | `cmpChartData[].客单价` | 同上 | `avg_bill_value` | `avg(received_total)` | 同上 | > BarChart双Y轴:左轴=账单数+实收,右轴=客单价。PieChart显示会员/非会员账单数占比。 ### 区块3:会员与非会员指标对比(FilterableTable,全量不分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 客户类型 | `cmpRows[].customer_type` | /member/comparison | `customer_type` | — | bill_fact | | 账单数 | `cmpRows[].bill_count` | 同上 | `bill_count` | `count(*)` | 同上 | | 实收 | `cmpRows[].received` | 同上 | `received` | `sum(received_total)` | 同上 | | 客单价 | `cmpRows[].avg_bill_value` | 同上 | `avg_bill_value` | `avg(received_total)` | 同上 | | 优惠率 | `cmpRows[].discount_rate_pct` | 同上 | `discount_rate_pct` | `sum(discount_total) / sum(consumption) * 100` | 同上 | | 理论毛利率 | `cmpRows[].theoretical_margin_pct` | 同上 | `theoretical_margin_pct` | `sum(theoretical_profit) / sum(received_total) * 100` | 同上 | > 仅2行数据(会员/非会员),客户端排序有效。 ### 区块4:门店复购率(FilterableTable,全量不分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 门店 | `repeatRows[].store_name` | /member/repeat | `store_name` | — | v_store_repeat_summary_monthly | | 识别会员数 | `repeatRows[].identified_members` | 同上 | `identified_members` | — | 同上 | | 复购会员数 | `repeatRows[].repeat_members` | 同上 | `repeat_members` | — | 同上 | | 复购率 | `repeatRows[].repeat_rate_pct` | 同上 | `repeat_rate_pct` | — | 同上 | | 人均消费次数 | `repeatRows[].avg_orders` | 同上 | `avg_orders` | — | 同上 | | 复购收入占比 | `repeatRows[].repeat_revenue_share_pct` | 同上 | `repeat_revenue_share_pct` | — | 同上 | > **筛选**:`filterKey="store_name"` 按门店筛选。**排序**:前端 `sortOptions` 客户端排序(全量数据,有效)。后端固定 `ORDER BY repeat_rate_pct DESC`。 > **复购率颜色**:≥50%绿色,≥30%黄色,<30%红色。 ### 发现的问题汇总 > MemberPage 无发现新问题。页面结构简洁,API与前端字段匹配,全量数据统计准确,日期无特殊格式化需求。 --- ## 十一、页面9:商品SKU管理 SKUPage > 单页面无子组件文件,5个区块(SKU概览+ABC分布图+品类分布+SKU明细+长尾治理),2个API(全部GET)。 > 核心数据源:`analytics.mv_dish_sku_abc_monthly`(菜品SKU ABC分析月度物化视图,按month_start过滤)、`analytics.category_summary`(品类汇总表,无月份过滤)。 ### 区块1:SKU概览(4个MetricCard) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 总SKU数 | `totalSku` | /sku/abc | 前端计算 | `data.length` | mv_dish_sku_abc_monthly | | A类SKU(核心) | `abcSummary['A-核心']?.sku_count` | 同上 | 前端计算 | `reduce(abc_class='A-核心').count` | 同上 | | C类SKU(长尾) | `cClassCount` | 同上 | 前端计算 | `abcSummary['C-长尾']?.sku_count` | 同上 | | 低价值SKU(<¥1000) | `lowValueSku` | 同上 | 前端计算 | `filter(received_amount < 1000).length` | 同上 | > **全量数据**:`/sku/abc` 不分页,返回全量SKU列表,前端统计全局准确。 ### 区块2:SKU ABC分布(PieChart) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | ABC分类 | `abcPieData[].name` | /sku/abc | `abc_class` | — | mv_dish_sku_abc_monthly | | SKU数量 | `abcPieData[].value` | 同上 | 前端计算 | `reduce(sku_count++)` | 同上 | | 实收 | `abcPieData[].received` | 同上 | 前端计算 | `reduce(received_amount +=)` | 同上 | > PieChart按ABC分类显示SKU数量分布,Tooltip同时显示SKU数和实收金额。 ### 区块3:品类实收分布(BarChart) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 品类名 | `catRows[].category_name` | /sku/category | `category_name` | — | category_summary | | 实收 | `catRows[].amount` | 同上 | `amount` | — | 同上 | > BarChart取 `catRows.slice(0, 10)` 前10个品类。水平柱状图,Y轴=品类名,X轴=金额。 > ⚠️ **`/sku/category` 无月份过滤**:后端查询 `SELECT * FROM analytics.category_summary ORDER BY amount DESC`,不接受month参数,返回全量品类汇总。前端也未传month参数。如果品类数据需要按月过滤,此处存在口径不一致。 ### 区块4:SKU明细(FilterableTable,全量不分页) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 菜品名称 | `skuRows[].dish_name` | /sku/abc | `dish_name` | — | mv_dish_sku_abc_monthly | | 一级品类 | `skuRows[].category_level1` | 同上 | `category_level1` | — | 同上 | | ABC分类 | `skuRows[].abc_class` | 同上 | `abc_class` | — | 同上 | | 象限 | `skuRows[].sales_quadrant` | 同上 | `sales_quadrant` | — | 同上 | | 覆盖门店 | `skuRows[].store_count` | 同上 | `store_count` | — | 同上 | | 账单数 | `skuRows[].bill_count` | 同上 | `bill_count` | — | 同上 | | 实收 | `skuRows[].received_amount` | 同上 | `received_amount` | — | 同上 | | 占比 | `skuRows[].revenue_share_pct` | 同上 | `revenue_share_pct` | — | 同上 | | 优惠率 | `skuRows[].discount_rate_pct` | 同上 | `discount_rate_pct` | — | 同上 | > **筛选**:`filterKey="abc_class"` 按ABC分类筛选。**排序**:前端客户端排序(全量数据,有效)。后端固定 `ORDER BY cumulative_revenue_share`。 ### 区块5:长尾治理建议(3个可展开区块) #### 5a. 低价值SKU(received_amount < 1000) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 菜品名称 | `lowValueSkuRows[].dish_name` | /sku/abc | `dish_name` | — | mv_dish_sku_abc_monthly | | 一级品类 | `lowValueSkuRows[].category_level1` | 同上 | `category_level1` | — | 同上 | | ABC分类 | `lowValueSkuRows[].abc_class` | 同上 | `abc_class` | — | 同上 | | 覆盖门店 | `lowValueSkuRows[].store_count` | 同上 | `store_count` | — | 同上 | | 账单数 | `lowValueSkuRows[].bill_count` | 同上 | `bill_count` | — | 同上 | | 实收 | `lowValueSkuRows[].received_amount` | 同上 | `received_amount` | — | 同上 | > 前端筛选:`received_amount < 1000`,按 `received_amount` 升序排列。`filterKey="category_level1"` 按品类筛选。 #### 5b. 单店专属SKU(store_count <= 1) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 同5a | `singleStoreSkuRows[]` | /sku/abc | 同上 | `filter(store_count <= 1)` | 同上 | #### 5c. 合并/归档候选(套餐/赠品/餐具类) | 显示指标 | 前端变量 | API | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------|---------| | 同5a | `mergeCandidateRows[]` | /sku/abc | 同上 | `filter(dish_name includes 套餐/赠/餐具/打包/纸巾/调料包)` | 同上 | > 前端按菜品名称关键词筛选合并候选,按 `received_amount` 升序排列。 --- ## 十二、页面10:企业协同管理 EnterprisePage > 单页面10个Tab,13个API(12个GET + 2个mutation),无MonthPicker。 > 核心数据源:`analytics.v3_*` 系列业务管理表(供应商、采购订单、审批流、消息、HR、培训、营销、财务、闭环、审计)。 ### Tab1:供应链SRM(supplier) | API | 数据源表 | 说明 | |-----|---------|------| | /enterprise/suppliers | analytics.v3_supplier_master | 供应商主数据,按overall_score降序 | | /enterprise/purchase-orders | analytics.v3_purchase_order | 采购订单,按created_at降序,LIMIT 100 | > MetricCard:供应商总数、战略供应商、采购订单数、淘汰供应商。 > PieChart:供应商分级分布(战略/优选/合格/淘汰)。 > 日期格式化:`r.order_date?.substring(0, 10)` ✅ ### Tab2:供应商对标(benchmark) | API | 数据源表 | 说明 | |-----|---------|------| | /enterprise/supplier-benchmark | v3_purchase_order_item + v3_purchase_order + v3_supplier_master | 跨供应商同SKU价格对比,CTE查询 | > MetricCard:对标SKU数、价格差异大(>20%)、有优化空间(10-20%)、潜在节约。 > 后端返回 `{ summary, benchmarks }` 结构,前端正确解构。 ### Tab3:审批流(approval) | API | 数据源表 | 说明 | |-----|---------|------| | /enterprise/approvals | analytics.v3_approval_request | 审批请求列表,LIMIT 100 | | POST /enterprise/approvals/:id/approve | 同上 + v3_approval_step | 审批操作mutation | > MetricCard:审批总数、待审批、已审批。 > 日期格式化:`(r.created_at || '').substring(0, 19).replace('T', ' ')` ✅ ### Tab4:消息中心(notification) | API | 数据源表 | 说明 | |-----|---------|------| | /enterprise/notifications | analytics.v3_notification | 按user_id或user_role过滤,LIMIT 50 | | PATCH /enterprise/notifications/:id/read | 同上 | 标记已读mutation | > MetricCard:消息总数、未读、已读。 ### Tab5:HR人才(hr) | API | 数据源表 | 说明 | |-----|---------|------| | /enterprise/hr/talent-matrix | analytics.dim_employee + analytics.store_task | 人才九宫格(绩效×潜力),LIMIT 200 | > MetricCard:人才总数、明星员工、核心员工、潜力员工。 > ⚠️ **potential_score 硬编码为60**:后端 `COALESCE(60, 60) AS potential_score`,这意味着所有员工的潜力分都是60,九宫格的"潜力"维度实际上没有区分度。只有performance_score有变化(基于store_task完成情况)。 ### Tab6:培训LMS(lms) | API | 数据源表 | 说明 | |-----|---------|------| | /enterprise/training/courses | analytics.v3_training_course | 课程列表 | | /enterprise/training/records | analytics.v3_learning_record | 学习记录,LIMIT 100 | > MetricCard:课程总数、学习记录数、已完成、未完成。 > 日期格式化:`(r.created_at || '').substring(0, 10)` ✅ ### Tab7:营销MA(marketing) | API | 数据源表 | 说明 | |-----|---------|------| | /enterprise/campaigns | analytics.v3_marketing_campaign | 营销活动列表,LIMIT 100 | | /enterprise/coupons | analytics.v3_coupon | 券码列表,LIMIT 100 | > MetricCard:活动总数、进行中、券码总数、已核销。 > 日期格式化:`(r.start_date || '').substring(0, 10)` ✅ ### Tab8:财务ERP(finance) | API | 数据源表 | 说明 | |-----|---------|------| | /enterprise/budgets | analytics.v3_budget | 按年度过滤,默认当前年 | > MetricCard:预算总数、总预算、总实际。 > 日期格式化:`r.month?.substring(0, 10)` ✅ ### Tab9:闭环验证(loop) | API | 数据源表 | 说明 | |-----|---------|------| | /enterprise/loop-verification | analytics.v3_loop_verification | 闭环验证列表,LIMIT 100 | > MetricCard:验证总数、已完成、需升级。 > 日期格式化:`(r.check_date || '').substring(0, 10)` ✅ ### Tab10:审计日志(audit) | API | 数据源表 | 说明 | |-----|---------|------| | /enterprise/audit-logs | analytics.v3_audit_log | 审计日志,LIMIT 100 | > 日期格式化:`(r.created_at || '').substring(0, 19).replace('T', ' ')` ✅ --- ## 十三、页面11:态势感知 SituationalAwarenessPage > 单页面4个Tab(健康度/预警/关联/趋势),4个API(全部GET),有MonthPicker。 > 核心数据源:`analytics.mv_store_risk_rating_monthly`、`analytics.mv_store_theoretical_actual_cost_monthly`、`analytics.mv_store_repeat_summary_monthly`、`analytics.store_task`、`analytics.mv_daily_revenue`、`public.dish_cost_analysis_summary`、`salary_detail_records`、`analytics.mv_store_platform_economics_monthly`、`bill_records`、`mv_bill_hourly`、`mv_store_hourly_staffing`、`public.store_name_mapping`。 ### Tab1:健康度评分(health) #### API: /situational-awareness/health-score | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 门店 | `rows[].store_name` | `store_name` | — | mv_store_risk_rating_monthly | | 健康分 | `rows[].health_score` | `health_score` | 8维加权求和(满分100) | 多表CTE | | 状态 | `rows[].health_status` | `health_status` | ≥75健康, ≥55亚健康, <55需干预 | — | | 风险 | `rows[].risk_level` | `risk_level` | — | mv_store_risk_rating_monthly | | 实收 | `rows[].received` | `received` | — | 同上 | | 客单价 | `rows[].avg_bill_value` | `avg_bill_value` | — | 同上 | | 毛利率 | `rows[].avg_margin` | `avg_margin` | `COALESCE(theoretical_margin_pct, 0)` | 同上 | | 成本差异率 | `rows[].avg_cost_variance_pct` | `avg_cost_variance_pct` | `avg(LEAST(variance_to_theoretical_pct, 50))` | mv_store_theoretical_actual_cost_monthly | | 复购率 | `rows[].repeat_rate_pct` | `repeat_rate_pct` | `max(repeat_rate_pct)` | mv_store_repeat_summary_monthly | | 任务完成率 | `rows[].task_completion_rate` | `task_completion_rate` | `count(status IN ('已验收','已回滚')) / count(*) * 100` | store_task | > **8维评分体系**:营收规模(20)、成本控制(20)、毛利率(15)、风险等级(10)、复购率(10)、会员占比(10)、任务完成(10)、客单价(5) = 100分。 > **数据权限**:支持 `getDataScope` 按角色过滤门店。 > **弹窗详情**:点击行展示RadarChart雷达图 + 各维度进度条 + 原始指标 + 跳转门店详情按钮。 > **全量数据**:不分页,客户端排序和筛选。 ### Tab2:预警中心(alerts) #### API: /situational-awareness/alerts | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------| | 总预警 | `d.total` | `total` | `alerts.length` | 多源 | | 红色预警 | `d.red` | `red` | `filter(level='red').length` | 多源 | | 橙色预警 | `d.orange` | `orange` | `filter(level='orange').length` | 多源 | | 预警列表 | `alerts[]` | `alerts` | 5类预警合并 | 多源 | > **5类预警**(Promise.all并行查询): > 1. **营收异动**(revenue, red):日营收 < 月均70%,`mv_daily_revenue`,LIMIT 10 > 2. **成本异动**(cost, red):成本差异率>30%,`dish_cost_analysis_summary`,LIMIT 10 > 3. **人力异动**(hr, red/orange):考勤异常>3人,`salary_detail_records`,LIMIT 10 > 4. **平台依赖**(platform, orange):外卖占比>40%,`mv_store_platform_economics_monthly`,LIMIT 10 > 5. **任务逾期**(task, red/orange):逾期任务,`store_task`,LIMIT 10 > > **前端分页**:客户端分页 PAGE_SIZE=20,使用 `SearchSelect` 做门店/类型/等级三维筛选。 > ⚠️ **预警查询部分不使用month参数**:revenue和cost预警查询不传month参数,使用全局最新数据。 ### Tab3:关联分析(correlation) #### API: /situational-awareness/correlation 返回4组关联分析数据: | 数据组 | 前端变量 | API字段 | 数据源表 | 说明 | |--------|---------|---------|---------|------| | 成本-风险 | `costRiskRows` | `cost_risk` | mv_store_risk_rating_monthly + mv_store_theoretical_actual_cost_monthly | 成本差异率×风险等级关联状态 | | 渠道-会员 | `channelRiskRows` | `channel_risk` | mv_store_risk_rating_monthly + mv_store_platform_economics_monthly + mv_store_repeat_summary_monthly | 平台占比×会员占比三角分析 | | 考勤-营收 | `hrRevenueRows` | `hr_revenue` | salary_detail_records + v_store_scorecard + store_name_mapping | 出勤率×营收人效关联 | | 客流-人力 | `staffingRows` | `staffing_efficiency` | mv_bill_hourly + mv_store_hourly_staffing | 每小时账单×在岗人数匹配度 | > **散点图**:成本-风险关联,X=日均实收,Y=超耗率,颜色=风险等级。 > **4个FilterableTable**:每个都有 `filterKey` + `sortOptions` + `statusFilterKey` + `statusOptions`,客户端排序。 > ⚠️ **客流-人力匹配查询有容错处理**:`mv_store_hourly_staffing` 可能不存在,try-catch返回空数组。 > ⚠️ **考勤-营收关联不使用month参数**:`salary_detail_records` 查询无month过滤,使用全量数据。`v_store_scorecard` 也无month过滤。 ### Tab4:趋势预测(forecast) #### API: /situational-awareness/forecast | 数据组 | 前端变量 | API字段 | 数据源表 | 说明 | |--------|---------|---------|---------|------| | 客流预测 | `trafficRows` | `traffic_forecast` | bill_records | 按工作日/周末×小时P85分位预测 | | 成本趋势 | `costTrendRows` | `cost_trend` | dish_cost_analysis_summary | 成本差异恶化TOP20 | | 人员流失 | `turnoverRows` | `turnover_alert` | salary_detail_records | 有离职的门店流失率排名 | > **客流预测**:从 `bill_records` 按小时×日类型聚合,计算 avg/P50/P85/波动率,稳定性分级。 > **LineChart**:工作日和周末分别画两条LineChart(avg_bills + p85_bills + p50_bills)。 > ⚠️ **成本趋势查询不使用month参数**:`dish_cost_analysis_summary` 查询无month/import_id过滤,使用全量最新数据。LIMIT 20。 > **人员流失**:按month参数过滤 leave_date 和 hire_date。 --- ## 十四、页面12:门店详情 StoreDetailPage > 单页面7个Tab(概览/餐段/品类/成本/会员/异常/任务),8个API(全部GET),有MonthPicker。 > 通过URL参数 `:code` 获取门店编码,复用 `/situational-awareness/health-score` API 获取健康度数据。 > 核心数据源:`bill_records`、`analytics.mv_store_risk_rating_monthly`、`analytics.mv_store_platform_economics_monthly`、`analytics.mv_store_benchmark_composite_monthly`、`analytics.mv_store_action_priority_deep_monthly`、`analytics.bill_fact`、`analytics.mv_store_category_mix_monthly`、`analytics.mv_store_theoretical_actual_cost_monthly`、`analytics.mv_inventory_cost_classified_monthly`、`analytics.mv_store_member_opportunity_monthly`、`analytics.mv_store_repeat_summary_monthly`、`analytics.v_store_member_monthly_activity`、`mv_risk_anomaly`、`analytics.dim_store`。 ### API总览 | API | Tab | 数据源表 | 说明 | |-----|-----|---------|------| | /stores/:code | 全局 | bill_records + mv_store_risk_rating_monthly + mv_store_platform_economics_monthly + mv_store_benchmark_composite_monthly + mv_store_action_priority_deep_monthly | 门店scorecard+风险+平台+基准+行动优先级 | | /situational-awareness/health-score | 概览 | 同SituationalAwarenessPage Tab1 | 复用健康度API,前端filter by store_code | | /stores/:code/daily | 概览 | bill_records | 日度实收趋势 | | /stores/:code/meal-period | 餐段 | analytics.bill_fact | 餐段分析+全网对标 | | /stores/:code/category-mix | 品类 | analytics.mv_store_category_mix_monthly | 门店vs公司品类结构 | | /stores/:code/cost | 成本 | mv_store_theoretical_actual_cost_monthly + mv_inventory_cost_classified_monthly | 成本概览+原料分类对标 | | /stores/:code/member | 会员 | mv_store_member_opportunity_monthly + mv_store_repeat_summary_monthly + v_store_member_monthly_activity | 会员占比+复购+月度活跃 | | /stores/:code/anomalies | 异常 | mv_risk_anomaly | 异常账单分页查询 | | /tasks | 任务 | analytics.store_task | 门店任务列表 | ### Tab1:概览(overview) | 显示指标 | 前端变量 | API | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 门店名 | `sc.store_name` | /stores/:code | `scorecard.store_name` | bill_records | | 健康分 | `health.health_score` | /situational-awareness/health-score | `health_score` | 多表CTE | | 风险等级 | `risk.risk_level` | /stores/:code | `risk.risk_level` | mv_store_risk_rating_monthly | | 经营象限 | `quadrant` | 同上 | 前端计算 | 日均营收≥18642 & 毛利率≥70% | | 实收 | `sc.received` | 同上 | `scorecard.received` | bill_records | | 账单数 | `sc.bill_count` | 同上 | `scorecard.bill_count` | 同上 | | 客单价 | `sc.avg_bill_value` | 同上 | `scorecard.avg_bill_value` | 同上 | | 优惠率 | `sc.discount_rate_pct` | 同上 | `scorecard.discount_rate_pct` | 同上 | | 理论毛利率 | `sc.theoretical_margin_pct` | 同上 | `scorecard.theoretical_margin_pct` | 同上 | | 会员占比 | `sc.member_bill_share_pct` | 同上 | `scorecard.member_bill_share_pct` | 同上 | | 日均实收 | `risk.avg_daily_received` | 同上 | `risk.avg_daily_received` | mv_store_risk_rating_monthly | | 平台实收 | `sd.platform.*` | 同上 | `platform.*` | mv_store_platform_economics_monthly | > **健康度雷达图**:复用SituationalAwarenessPage的8维评分体系,RadarChart + 进度条。 > **日度趋势**:LineChart显示日实收和客单价。X轴 `business_date.substring(5, 10)` ✅。 > **偏差指标**:前端硬编码公司均值 `{ daily_rev: 20144, bill: 36.8, ... }`,与后端计算的中位数(18642)不一致。 ### Tab2:餐段分析(meal) | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|-----|---------|---------| | 餐段 | `mealRows[].meal_period` | `meal_period` | — | bill_fact | | 账单数 | `mealRows[].bill_count` | `bill_count` | — | 同上 | | 实收 | `mealRows[].received` | `received` | `sum(received_total)` | 同上 | | 平均单价 | `mealRows[].avg_bill` | `avg_bill` | `sum(received_total)/count(*)` | 同上 | | 全网均价 | `mealRows[].network_avg_bill` | `network_avg_bill` | 全网同餐段均值 | 同上 | | vs全网 | `mealRows[].vs_network_pct` | `vs_network_pct` | `(avg_bill/network_avg_bill - 1) * 100` | — | > BarChart + FilterableTable,客户端排序。 ### Tab3:品类结构(category) | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 门店品类 | `cat.store.*` | `store.*` | mv_store_category_mix_monthly | | 公司品类 | `cat.company.*` | `company.*` | 同上(全量汇总) | > PieChart(门店品类占比) + BarChart(门店vs公司占比对比) + 4个MetricCard。 > 品类包括:兰州拉面、西式简餐、外卖套餐、冷菜、丝路美食。 ### Tab4:成本分析(cost) | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 理论成本率 | `cost.cost.theoretical_cost_rate_pct` | `theoretical_cost_rate_pct` | mv_store_theoretical_actual_cost_monthly | | 实际成本率 | `cost.cost.actual_food_cost_rate_pct` | `actual_food_cost_rate_pct` | 同上 | | 差异率 | `cost.cost.variance_to_theoretical_pct` | `variance_to_theoretical_pct` | 同上 | | 差异等级 | `cost.cost.variance_level` | `variance_level` | 同上 | | 原料分类 | `cost.categories[]` | `categories` | mv_inventory_cost_classified_monthly | > FilterableTable展示原料分类成本对标,含消耗金额、实际占比、公司基准、差异。 ### Tab5:会员分析(member) | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 会员占比 | `member.opportunity.member_share_pct` | `opportunity.member_share_pct` | mv_store_member_opportunity_monthly | | 公司均值 | `member.opportunity.company_member_share_pct` | `opportunity.company_member_share_pct` | 同上 | | 复购率 | `member.repeat.repeat_rate_pct` | `repeat.repeat_rate_pct` | mv_store_repeat_summary_monthly | | 复购收入占比 | `member.repeat.repeat_revenue_share_pct` | `repeat.repeat_revenue_share_pct` | 同上 | | 活跃会员数 | `member.monthly.member_count` | `monthly.member_count` | v_store_member_monthly_activity | | 人均订单 | `member.monthly.avg_orders` | `monthly.avg_orders` | 同上 | | 人均消费 | `member.monthly.avg_received` | `monthly.avg_received` | 同上 | > 提升机会文案:`member.opportunity.conversion_bill_scenario` + `revenue_uplift_scenario`。 ### Tab6:异常账单(anomaly) | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 账单号 | `anomalyRows[].bill_no` | `bill_no` | mv_risk_anomaly | | 餐段 | `anomalyRows[].meal_period` | `meal_period` | 同上 | | 消费额 | `anomalyRows[].consumption` | `consumption` | 同上 | | 优惠 | `anomalyRows[].discount_total` | `discount_total` | 同上 | | 实收 | `anomalyRows[].received_total` | `received_total` | 同上 | | 异常原因 | `anomalyRows[].anomaly_reason` | `anomaly_reason` | 同上 | | 结账时间 | `anomalyRows[].closed_at` | `closed_at` | 同上 | > 后端分页查询,前端 `page_size=200` 一次性加载。日期格式化:`r.closed_at?.substring(0, 16)` ✅。 ### Tab7:任务列表(tasks) | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|-----|---------|---------| | 问题指标 | `tasks[].problem_indicator` | `problem_indicator` | store_task | | 优先级 | `tasks[].priority` | `priority` | 同上 | | 行动要求 | `tasks[].action_required` | `action_required` | 同上 | | 状态 | `tasks[].status` | `status` | 同上 | | 截止日 | `tasks[].deadline` | `deadline` | 同上 | > 日期格式化:`r.deadline?.substring(0, 10)` ✅。点击行跳转 `/tasks/:task_id`。 --- ## 十五、页面13:门店分级与风险等级 StoreGradePage > 单页面2个Tab(门店分级/关停测算),3个API(2 GET + 1 POST mutation),有月份选择器(原生 `` 而非 MonthPicker 组件)。 > 核心数据源:`analytics.v3_store_grade`、`analytics.mv_store_risk_rating`、`analytics.v3_store_monthly_target`、`analytics.dim_store`、`analytics.mv_store_operating_expense_monthly`、`analytics.mv_store_area_efficiency_monthly`。 ### API总览 | API | Tab | 方法 | 数据源表 | 说明 | |-----|-----|------|---------|------| | /store-grade/grades | 分级 | GET | v3_store_grade + mv_store_risk_rating | 分级列表+风险详情 | | /store-grade/auto-grade | 分级 | POST | dim_store + mv_store_risk_rating + v3_store_monthly_target → v3_store_grade | 自动分级计算并写入 | | /store-grade/closure-analysis | 关停 | GET | v3_store_grade + mv_store_risk_rating + mv_store_operating_expense_monthly + mv_store_area_efficiency_monthly | 关停成本测算 | ### Tab1:门店分级(grade) #### API: /store-grade/grades | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 门店编码 | `r.store_code` | `store_code` | — | v3_store_grade | | 门店名称 | `r.store_name` | `store_name` | — | 同上 | | 区域 | `r.region` | `region` | — | 同上 | | 分级 | `r.grade` | `grade` | A≥85, B≥70, C≥50, D<50 | auto-grade计算 | | 综合分 | `r.overall_score` | `overall_score` | 达成率×0.4+毛利率×0.25+会员占比×0.15+风险×0.2 | auto-grade计算 | | 营收达成率 | `r.revenue_achievement_pct` | `revenue_achievement_pct` | `revenue / revenue_target × 100` | auto-grade计算 | | 风险等级 | `r.risk_level` | `risk_level` | — | mv_store_risk_rating | | 分级原因 | `r.reason` | `reason` | — | auto-grade写入 | | 营收 | `r.revenue` | `revenue` | — | mv_store_risk_rating | | 毛利率 | `r.margin_pct` | `margin_pct` | — | 同上 | | 优惠率 | `r.discount_rate` | `discount_rate` | — | 同上 | | 异常率 | `r.anomaly_rate` | `anomaly_rate` | — | 同上 | | 会员占比 | `r.member_pct` | `member_pct` | — | 同上 | | 客单价 | `r.avg_bill_value` | `avg_bill_value` | — | 同上 | | 活跃天数 | `r.active_days` | `active_days` | — | 同上 | > **自动分级公式**:overallScore = achievementScore×0.4 + marginScore×0.25 + memberScore×0.15 + riskScore×0.2 > - achievementScore = min(achievement, 100) > - marginScore = min(margin_pct/50×100, 100) > - memberScore = min(member_pct/50×100, 100) > - riskScore = 红色30, 黄色60, 绿色100 > > **弹窗详情**:RadarChart 4维(达成率/综合分/毛利率/异常率) + 13项指标列表。 > **PieChart**:分级分布 + 风险等级分布。 > **FilterableTable**:filterKey=grade, sortOptions=综合分/达成率/门店。 #### API: /store-grade/auto-grade (POST mutation) > 前端点击"自动分级"按钮触发 POST,后端读取 `dim_store` + `mv_store_risk_rating` + `v3_store_monthly_target`,计算综合分和分级,写入 `v3_store_grade` 表(UPSERT)。成功后前端 invalidateQueries 刷新列表。 ### Tab2:关停测算(closure) #### API: /store-grade/closure-analysis | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 月收入 | `r.monthly_revenue` | `monthly_revenue` | — | mv_store_risk_rating | | 月利润 | `r.monthly_profit` | `monthly_profit` | `actual_store_contribution` | mv_store_operating_expense_monthly | | 月租 | `r.monthly_rent` | `monthly_rent` | `rent_expense` | 同上 | | 月人工 | `r.monthly_wage` | `monthly_wage` | `wage_expense` | 同上 | | 关停成本 | `r.total_closure_cost` | `total_closure_cost` | `wage×1.5 + rent×2 + revenue×0.05` | — | | 遣散费 | — | `severance_cost` | `monthly_wage × 1.5` | — | | 违约金 | — | `lease_termination_cost` | `monthly_rent × 2` | — | | 资产处置 | — | `asset_disposal_cost` | `monthly_revenue × 0.05` | — | | 12月亏损 | `r.projected_12m_loss` | `projected_12m_loss` | `monthly_profit < 0 ? monthly_profit × 12 : 0` | — | | 月节省 | `r.monthly_savings_if_closed` | `monthly_savings_if_closed` | `monthly_profit < 0 ? abs(monthly_profit) : 0` | — | | 回本月数 | `r.payback_months` | `payback_months` | `total_closure_cost / abs(monthly_profit)` | — | | 建议 | `r.recommendation` | `recommendation` | 亏损+D级→关停, 亏损+C级→整改, 微利+低分→重点整改, else→持续经营 | — | | 原因 | `r.reason` | `reason` | — | — | > **汇总指标**:total_stores, recommend_close, recommend_rectify, total_closure_cost, total_projected_loss, total_monthly_savings。 > **PieChart**:关停建议分布(建议关停/整改观察/持续经营)。 > **FilterableTable**:filterKey=recommendation, sortOptions=月利润/关停成本/12月亏损/回本月数/综合分。 --- ## 十六、页面14:平台与优惠管理 PlatformPage > 单页面无Tab,2个API(全部GET),有MonthPicker。 > 核心数据源:`analytics.mv_store_platform_economics_monthly`、`analytics.bill_fact`。 ### API总览 | API | 数据源表 | 说明 | |-----|---------|------| | /platform/economics | mv_store_platform_economics_monthly | 门店三平台经济性数据 | | /marketing/plans | bill_fact | 营销方案聚合分析 | ### Block1:平台概览 #### API: /platform/economics | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 平台总实收 | `totalPlatformReceived` | — | 前端聚合:`Σ(meituan+taobao+jd)` | mv_store_platform_economics_monthly | | 合并加权成本率 | `blendedCostRate` | — | 前端计算:`(Σ折扣+Σ佣金) / Σ实收 × 100` | 同上 | | 高成本门店 | `highCostStores.length` | — | 前端过滤:任一平台cost_rate≥38% | 同上 | | 营销方案数 | `marketingRows.length` | — | 来自/marketing/plans | bill_fact | > 后端 `SELECT *` 返回全字段,前端聚合计算。无分页,全量返回。 > **平台字段**:`meituan_received/discount/commission/cost_rate_pct`、`taobao_*`、`jd_*`。 ### Block2:三平台对比图 > BarChart:美团/淘宝/京东三组,每组3条柱(实收/折扣/佣金)。前端聚合 `storesWithPlatform` 数据。 ### Block3:门店平台经济性明细 > FilterableTable:filterKey=store_name, sortOptions=美团实收/美团成本率/淘宝实收/京东实收/门店。 > **成本率颜色**:≥38%红色, 35-38%黄色, <35%绿色。 > **经济性最优列**:前端计算三平台中成本率最低的平台,显示名称和与最高的差距。 ### Block4:营销方案ROI #### API: /marketing/plans | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 营销方案 | `r.marketing_plan` | `marketing_plan` | — | bill_fact | | 账单数 | `r.bill_count` | `bill_count` | `count(*)` | 同上 | | 消费额 | `r.consumption` | `consumption` | `sum(consumption)` | 同上 | | 优惠额 | `r.discounts` | `discounts` | `sum(discount_total)` | 同上 | | 实收 | `r.received` | `received` | `sum(received_total)` | 同上 | | 客单价 | `r.avg_bill_value` | `avg_bill_value` | `avg(received_total)` | 同上 | | 优惠率 | `r.discount_rate_pct` | `discount_rate_pct` | `sum(discount_total)/sum(consumption)×100` | — | | 理论毛利率 | `r.theoretical_margin_pct` | `theoretical_margin_pct` | `sum(theoretical_profit)/sum(received_total)×100` | — | > 前端Top5营销方案卡片展示 + FilterableTable完整列表。 > **优惠率颜色**:≥25%红色, 15-25%黄色, <15%绿色。 > **毛利率颜色**:≥70%绿色, 60-70%黄色, <60%红色。 --- ## 十七、页面15:营收分析 RevenuePage > 单页面无Tab,4个API(全部GET),有MonthPicker。 > 核心数据源:`analytics.v_store_daily`、`analytics.v_channel_daily`、`analytics.v_meal_period_daily`。 ### API总览 | API | 数据源表 | 说明 | |-----|---------|------| | /revenue/daily-summary | v_store_daily | 日度营收汇总(全门店合计) | | /revenue/channel | v_channel_daily | 渠道日度收入结构 | | /revenue/meal-period | v_meal_period_daily | 时段收入分布 | | /revenue/store-ranking | v_store_daily | 门店营收排名 | ### Block1:核心指标 #### API: /revenue/daily-summary | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 营业收入 | `totalConsumption` | — | 前端聚合:`Σ consumption` | v_store_daily | | 优惠总额 | `totalDiscounts` | — | 前端聚合:`Σ discounts` | 同上 | | 实收总额 | `totalReceived` | — | 前端聚合:`Σ received` | 同上 | | 账单数 | `totalBills` | — | 前端聚合:`Σ bill_count` | 同上 | | 客单价 | `avgBillValue` | — | 前端计算:`totalReceived / totalBills` | — | | 优惠率 | `avgDiscountRate` | — | 前端计算:`totalDiscounts / (totalReceived + totalDiscounts) × 100` | — | | 客流量 | `totalGuests` | — | 前端聚合:`Σ guests` | v_store_daily | | 人均消费 | `avgGuestValue` | — | 前端计算:`totalReceived / totalGuests` | — | > 后端SQL:`sum(received + discounts) AS consumption, sum(received) AS received, sum(discounts) AS discounts, sum(bill_count), sum(guests)`,按 `business_date` 分组。 > **环比趋势**:前端计算 last7 vs prev7 的实收和账单数环比。 ### Block2:日度营收趋势 > ComposedChart:Bar(received) + Line(avg_bill) + Line(discount_rate),双Y轴。 > X轴 `business_date.substring(5, 10)` ✅。 ### Block3:渠道收入结构 + 渠道趋势 #### API: /revenue/channel | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------|---------| | 微信 | `channelTotals.wechat` | `wechat` | v_channel_daily | | 支付宝 | `channelTotals.alipay` | `alipay` | 同上 | | 美团外卖 | `channelTotals.meituan_delivery` | `meituan_delivery` | 同上 | | 美团堂食 | `channelTotals.meituan` | `meituan` | 同上 | | 淘宝外卖 | `channelTotals.taobao_delivery` | `taobao_delivery` | 同上 | | 抖音 | `channelTotals.douyin` | `douyin` | 同上 | | 现金 | `channelTotals.cash` | `cash` | 同上 | | 京东外卖 | `channelTotals.jd_delivery` | `jd_delivery` | 同上 | | 银联 | `channelTotals.unionpay` | `unionpay` | 同上 | | 挂账 | `channelTotals.credit` | `credit` | 同上 | > PieChart(渠道占比) + LineChart(前5大渠道日度趋势)。 > 前端聚合日度数据为渠道总量,按值排序。 ### Block4:时段收入分布 #### API: /revenue/meal-period | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 餐段 | `r.meal_period` | `meal_period` | — | v_meal_period_daily | | 账单数 | `r.bill_count` | `bill_count` | `sum(bill_count)` | 同上 | | 实收 | `r.received` | `received` | `sum(received)` | 同上 | | 客单价 | `r.avg_bill_value` | `avg_bill_value` | `sum(received)/sum(bill_count)` | — | > PieChart + 卡片列表展示各餐段收入和占比。 ### Block5:门店营收排名 #### API: /revenue/store-ranking | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 门店 | `r.store_name` | `store_name` | — | v_store_daily | | 实收 | `r.received` | `received` | `sum(received)` | 同上 | | 账单数 | `r.bill_count` | `bill_count` | `sum(bill_count)` | 同上 | | 客单价 | `r.avg_bill_value` | `avg_bill_value` | `sum(received)/sum(bill_count)` | — | | 优惠率 | `r.avg_discount_rate_pct` | `avg_discount_rate_pct` | `sum(discounts)/(sum(received)+sum(discounts))×100` | — | | 客流量 | `r.guests` | `guests` | `sum(guests)` | 同上 | | 理论毛利率 | `r.avg_theoretical_margin_pct` | `avg_theoretical_margin_pct` | `avg(theoretical_margin_pct)` | 同上 | > TOP10/BOTTOM10 水平BarChart(点击跳转门店详情) + FilterableTable完整列表。 > **优惠率颜色**:>25%红色, >20%黄色。 > 后端 `ORDER BY sum(received) DESC`,前端 slice(0,10) 和 slice(-10).reverse()。 --- ## 十八、页面16:区域经理工作台 RegionalPage > 单页面无Tab,5个API(4 GET + 1 PUT mutation),有MonthPicker。 > 核心数据源:`analytics.mv_store_risk_rating_monthly`、`analytics.mv_store_action_priority_deep_monthly`、`analytics.mv_store_benchmark_composite_monthly`、`analytics.bill_fact`、`analytics.dim_store`、`analytics.v_task_weekly_check`、`analytics.store_task`、`analytics.task_weekly_check`。 ### API总览 | API | 方法 | 数据源表 | 说明 | |-----|------|---------|------| | /stores/risk | GET | mv_store_risk_rating_monthly | 门店风险分级列表 | | /stores/priority | GET | mv_store_action_priority_deep_monthly | 门店行动优先级 | | /benchmark/composite | GET | mv_store_benchmark_composite_monthly | 同类门店基准对比 | | /region/summary | GET | bill_fact + dim_store + mv_store_risk_rating_monthly | 区域经营汇总 | | /tasks/weekly-check | GET | v_task_weekly_check | 周检任务列表 | | /tasks/:id/weekly-check | PUT | store_task + store_task_log + task_weekly_check | 周检录入 | ### Block1:汇总卡片 | 显示指标 | 前端变量 | 数据来源 | 计算公式 | |---------|---------|---------|---------| | 营业收入 | `Σ regionRows.total_consumption` | /region/summary | 前端聚合 | | 优惠 | `Σ regionRows.total_discount` | 同上 | 前端聚合 | | 实收 | `Σ regionRows.total_received` | 同上 | 前端聚合 | | 红黄门店数 | `riskRows.filter(risk_level !== '绿色').length` | /stores/risk | 前端过滤 | | P0+P1门店 | `p0p1.length` | /stores/priority | 前端过滤 action_priority.startsWith('P0'/'P1') | | 需到店检查 | `needCheck.length` | /tasks/weekly-check | 前端过滤 consecutive_no_improve_weeks ≥ 2 | | 周检待录入 | `weeklyRows.filter(!last_check_date).length` | 同上 | 前端过滤 | ### Block2:区域经营汇总 #### API: /region/summary | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 区域 | `r.region` | `region` | — | dim_store | | 门店数 | `r.store_count` | `store_count` | `count(DISTINCT store_code)` | bill_fact | | 营业收入 | `r.total_consumption` | `total_consumption` | `sum(consumption)` | 同上 | | 优惠 | `r.total_discount` | `total_discount` | `sum(discount_total)` | 同上 | | 实收 | `r.total_received` | `total_received` | `sum(received_total)` | 同上 | | 客单价 | `r.avg_bill_value` | `avg_bill_value` | `avg(received_total)` | 同上 | | 优惠率 | `r.avg_discount_rate` | `avg_discount_rate` | `sum(discount_total)/sum(consumption)×100` | — | | 毛利率 | `r.avg_margin_rate` | `avg_margin_rate` | `sum(theoretical_profit)/sum(received_total)×100` | — | | 异常率 | `r.avg_anomaly_rate` | `avg_anomaly_rate` | **硬编码 0** | — | | 红数 | `r.red_count` | `red_count` | `count(DISTINCT CASE WHEN risk_level='红色')` | mv_store_risk_rating_monthly | | 黄数 | `r.yellow_count` | `yellow_count` | 同上黄色 | 同上 | | 绿数 | `r.green_count` | `green_count` | 同上绿色 | 同上 | > FilterableTable,sortOptions=区域/营业收入/优惠/实收/客单价/门店数。 > **数据权限**:使用 `getDataScope(req)` + `scopeStoreFilter` 实现区域经理只能看自己区域。 ### Block3:门店风险分级 #### API: /stores/risk | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 门店 | `r.store_name` | `store_name` | mv_store_risk_rating_monthly | | 风险 | `r.risk_level` | `risk_level` | 同上 | | 实收 | `r.received` | `received` | 同上 | | 优惠率 | `r.discount_rate_pct` | `discount_rate_pct` | 同上 | | 毛利率 | `r.theoretical_margin_pct` | `theoretical_margin_pct` | 同上 | | 异常率 | `r.anomaly_rate_pct` | `anomaly_rate_pct` | 同上 | > 后端 `SELECT *` + `ORDER BY risk_level, received DESC`。前端风险筛选按钮(全部/红色/黄色/绿色)+ FilterableTable。 > 点击行跳转 `/stores/:store_code`。 ### Block4:需到店检查门店 #### API: /tasks/weekly-check | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 门店 | `r.store_name` | `store_name` | v_task_weekly_check | | 问题指标 | `r.problem_indicator` | `problem_indicator` | 同上 | | 连续未改善周数 | `r.consecutive_no_improve_weeks` | `consecutive_no_improve_weeks` | 同上 | | 任务状态 | `r.status` | `status` | 同上 | > 前端过滤 `consecutive_no_improve_weeks ≥ 2`。点击"录入周检"打开弹窗。 > **PUT API**:`/tasks/:id/weekly-check`,更新 `store_task.process_evidence/next_step`,插入 `store_task_log` 和 `task_weekly_check` 记录。 ### Block5:同类门店基准对比 #### API: /benchmark/composite | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 门店 | `r.store_name` | `store_name` | mv_store_benchmark_composite_monthly | | 标杆得分 | `r.benchmark_score` | `benchmark_score` | 同上 | | 实收 | `r.received` | `received` | 同上 | | 客单价 | `r.avg_bill_value` | `avg_bill_value` | 同上 | | 优惠率 | `r.discount_rate_pct` | `discount_rate_pct` | 同上 | | 毛利率 | `r.theoretical_margin_pct` | `theoretical_margin_pct` | 同上 | | 会员占比 | `r.member_bill_share_pct` | `member_bill_share_pct` | 同上 | > 后端 `SELECT * ORDER BY benchmark_score DESC`。**标杆得分颜色**:≥80绿色, ≥60黄色, <60红色。 --- ## 十九、页面17:门店选址分析 SiteSelectionPage > 单页面4个Tab(分段基准/复制评分/重叠风险/区域基准),5个API(全部GET),有MonthPicker。 > 核心数据源:`analytics.mv_store_site_profile_monthly`、`analytics.mv_site_segment_benchmark_monthly`、`analytics.mv_store_site_replication_monthly`、`analytics.mv_store_overlap_risk_monthly`、`analytics.mv_district_site_benchmark_monthly`。 ### API总览 | API | Tab | 数据源表 | 说明 | |-----|-----|---------|------| | /site-selection/profile | 全局 | mv_store_site_profile_monthly | 门店选址画像 | | /site-selection/segment-benchmark | 分段基准 | mv_site_segment_benchmark_monthly | 场景×面积分段基准 | | /site-selection/replication | 复制评分 | mv_store_site_replication_monthly | 门店复制评分 | | /site-selection/overlap-risk | 重叠风险 | mv_store_overlap_risk_monthly | 门店空间重叠风险 | | /site-selection/district-benchmark | 区域基准 | mv_district_site_benchmark_monthly | 区域选址基准 | ### 全局:概览指标 + 散点图 + 地图 #### API: /site-selection/profile | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 标准门店数 | `profileRows.length` | — | mv_store_site_profile_monthly | | 场景类型数 | `totalScenes` | — | 前端聚合 site_scene 去重 | | 高风险重叠对 | `highRiskCount` | — | 来自 /overlap-risk 前端过滤 | | 优先提炼原型 | `topReplication` | — | 来自 /replication 前端过滤 | > **StoreMap**:自定义地图组件,按场景类型颜色标注门店位置。 > **ScatterChart**:X=面积(㎡), Y=月坪效(元/㎡), Z=实收(气泡大小),按场景颜色区分。 > 后端 `WHERE business_type='标准门店' AND received>0`,`ORDER BY received DESC`。 ### Tab1:分段基准(benchmark) #### API: /site-selection/segment-benchmark | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 场景 | `r.site_scene` | `site_scene` | mv_site_segment_benchmark_monthly | | 面积段 | `r.area_band` | `area_band` | 同上 | | 门店数 | `r.store_count` | `store_count` | 同上 | | 平均面积 | `r.avg_area_sqm` | `avg_area_sqm` | 同上 | | 平均实收 | `r.avg_received` | `avg_received` | 同上 | | 中位实收 | `r.median_received` | `median_received` | 同上 | | 平均坪效 | `r.avg_received_per_sqm` | `avg_received_per_sqm` | 同上 | | 中位坪效 | `r.median_received_per_sqm` | `median_received_per_sqm` | 同上 | | 客单价 | `r.avg_bill_value` | `avg_bill_value` | 同上 | | 复购率 | `r.avg_repeat_rate_pct` | `avg_repeat_rate_pct` | 同上 | > FilterableTable,filterKey=site_scene,sortOptions=平均坪效/中位坪效/平均实收/场景。 > 后端 `ORDER BY avg_received_per_sqm DESC`。 > **门店画像明细**:同 profile API 数据,filterKey=site_scene,点击行跳转门店详情。 ### Tab2:复制评分(replication) #### API: /site-selection/replication | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 门店 | `r.store_name` | `store_name` | mv_store_site_replication_monthly | | 场景 | `r.site_scene` | `site_scene` | 同上 | | 面积 | `r.area_sqm` | `area_sqm` | 同上 | | 实收 | `r.received` | `received` | 同上 | | 坪效 | `r.monthly_received_per_sqm` | `monthly_received_per_sqm` | 同上 | | 最近门店 | `r.nearest_store_name` | `nearest_store_name` | 同上 | | 距离 | `r.nearest_distance_km` | `nearest_distance_km` | 同上 | | 复制评分 | `r.site_replication_score` | `site_replication_score` | 同上 | | 复制建议 | `r.replication_recommendation` | `replication_recommendation` | 同上 | | 空间建议 | `r.spatial_recommendation` | `spatial_recommendation` | 同上 | > FilterableTable,filterKey=replication_recommendation,sortOptions=复制评分/实收/坪效/距离。 > **复制评分颜色**:≥75绿色, ≥60蓝色, <40红色。 > **复制建议标签**:优先提炼选址原型(绿)、不宜作为选址标杆(红)、其他(蓝)。 > 后端 `ORDER BY site_replication_score DESC`。 ### Tab3:重叠风险(overlap) #### API: /site-selection/overlap-risk | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 门店A | `r.store_name_a` | `store_name_a` | mv_store_overlap_risk_monthly | | 门店B | `r.store_name_b` | `store_name_b` | 同上 | | 距离 | `r.distance_km` | `distance_km` | 同上 | | A实收 | `r.received_a` | `received_a` | 同上 | | B实收 | `r.received_b` | `received_b` | 同上 | | 风险等级 | `r.overlap_risk` | `overlap_risk` | 同上 | > 3个MetricCard(<1km/1-2km/2-3km)+ FilterableTable。 > filterKey=overlap_risk,sortOptions=距离/A实收/B实收。 > 后端 `ORDER BY distance_km`。 ### Tab4:区域基准(district) #### API: /site-selection/district-benchmark | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 区域 | `r.district` | `district` | mv_district_site_benchmark_monthly | | 门店数 | `r.store_count` | `store_count` | 同上 | | 平均面积 | `r.avg_area_sqm` | `avg_area_sqm` | 同上 | | 总实收 | `r.total_received` | `total_received` | 同上 | | 平均实收 | `r.avg_received` | `avg_received` | 同上 | | 中位实收 | `r.median_received` | `median_received` | 同上 | | 平均坪效 | `r.avg_received_per_sqm` | `avg_received_per_sqm` | 同上 | | 客单价 | `r.avg_bill_value` | `avg_bill_value` | 同上 | | 复购率 | `r.avg_repeat_rate_pct` | `avg_repeat_rate_pct` | 同上 | | P0数 | `r.p0_count` | `p0_count` | 同上 | | P1数 | `r.p1_count` | `p1_count` | 同上 | > BarChart(前15区域平均/中位实收对比) + FilterableTable。 > sortOptions=平均实收/平均坪效/总实收/区域。 > 后端 `ORDER BY total_received DESC`。 --- ## 二十、页面18:时间维度分析 TimePage > 单页面无Tab,3个API(全部GET),有MonthPicker。 > 核心数据源:`mv_time_weekday`、`mv_time_hourly`、`mv_channel_daily`。 ### API总览 | API | 数据源表 | 说明 | |-----|---------|------| | /time/weekday | mv_time_weekday | 星期维度分析 | | /time/hourly | mv_time_hourly | 小时维度分析 | | /channel | mv_channel_daily | 渠道日度趋势 | ### Block1:时间概览 | 显示指标 | 前端变量 | 数据来源 | 计算公式 | |---------|---------|---------|---------| | 高峰日 | `peakWeekday.weekday` | /time/weekday | 前端 reduce 找最大 received | | 低谷日 | `lowWeekday.weekday` | 同上 | 前端 reduce 找最小 received | | 高峰时段 | `peakHour.closing_hour` | /time/hourly | 前端 reduce 找最大 bill_count | | 渠道数 | `channelTotals.length` | /channel | 前端聚合非零渠道数 | ### Block2:星期维度分析 #### API: /time/weekday | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 星期 | `r.weekday` | `weekday` | mv_time_weekday | | 实收 | `r.received` | `received` | 同上 | | 账单数 | `r.bill_count` | `bill_count` | 同上 | | 客单价 | `r.avg_bill_value` | `avg_bill_value` | 同上 | > BarChart(实收+账单数)+ Line(客单价),双Y轴。后端 `WHERE month = to_char($1::date, 'YYYY-MM')`。 ### Block3:小时维度分析 #### API: /time/hourly | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 小时 | `r.closing_hour` | `closing_hour` | mv_time_hourly | | 账单数 | `r.bill_count` | `bill_count` | 同上 | | 实收 | `r.received` | `received` | 同上 | | 客单价 | `r.avg_bill_value` | `avg_bill_value` | 同上 | > AreaChart(实收+账单数),双Y轴。后端 `WHERE month = to_char($1::date, 'YYYY-MM')`。 ### Block4:渠道趋势 + 渠道汇总 #### API: /channel | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 日期 | `r.business_date` | `business_date` | mv_channel_daily | | 现金 | `r.cash` | `cash` | 同上 | | 支付宝 | `r.alipay` | `alipay` | 同上 | | 微信 | `r.wechat` | `wechat` | 同上 | | 美团到店 | `r.meituan` | `meituan` | 同上 | | 银联 | `r.unionpay` | `unionpay` | 同上 | | 抖音 | `r.douyin` | `douyin` | 同上 | | 挂账 | `r.credit` | `credit` | 同上 | | 京东到家 | `r.jd_delivery` | `jd_delivery` | 同上 | | 美团外卖 | `r.meituan_delivery` | `meituan_delivery` | 同上 | | 淘宝外卖 | `r.taobao_delivery` | `taobao_delivery` | 同上 | > LineChart(前6大渠道日度趋势)+ 渠道汇总卡片。X轴 `business_date.substring(5, 10)` ✅。 > **注意**:此API与 RevenuePage 的 `/revenue/channel` 数据源相同(`mv_channel_daily`),但查询无 schema 前缀(`mv_channel_daily` vs `analytics.v_channel_daily`)。 --- ## 二十一、页面19:指标字典 IndicatorsPage > 单页面无Tab,1个API(GET),无MonthPicker。 > 核心数据源:`analytics.indicator_dictionary`。 ### API: /tasks/indicators | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 指标名称 | `r.indicator_name` | `indicator_name` | indicator_dictionary | | 业务定义 | `r.business_definition` | `business_definition` | 同上 | | 计算公式 | `r.formula` | `formula` | 同上 | | 更新频率 | `r.update_frequency` | `update_frequency` | 同上 | | 负责人 | `r.owner` | `owner` | 同上 | | 黄线 | `r.yellow_threshold` | `yellow_threshold` | 同上 | | 红线 | `r.red_threshold` | `red_threshold` | 同上 | | 版本日期 | `r.version_date` | `version_date` | 同上 | > FilterableTable,filterKey=update_frequency,sortOptions=指标名称/更新频率/负责人。 > `version_date?.substring(0, 10)` ✅。后端 `SELECT * ORDER BY id`,无分页。 --- ## 二十二、页面20:任务管理 TasksPage > 单页面无Tab,1个API(GET),有MonthPicker + 优先级/状态筛选。 > 核心数据源:`analytics.store_task`。 ### API: /tasks | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 问题指标 | `r.problem_indicator` | `problem_indicator` | store_task | | 行动要求 | `r.action_required` | `action_required` | 同上 | | 负责人 | `r.owner` | `owner` | 同上 | | 截止日 | `r.deadline?.substring(0,10)` | `deadline` | 同上 | | 状态 | `r.status` | `status` | 同上 | | 优先级 | `r.priority` | `priority` | 同上 | | 门店 | `r.store_name` | `store_name` | 同上 | | 门店编码 | `r.store_code` | `store_code` | 同上 | > 后端支持分页(`parsePagination`),前端传 `page_size: 200`。 > 前端按 `store_code` 分组展示,每组内 FilterableTable。 > **筛选**:priority + status 参数传递到后端 WHERE 条件。 > `deadline?.substring(0, 10)` ✅。 --- ## 二十三、页面21:任务详情 TaskDetailPage > 单页面无Tab,2个API(1 GET + 1 GET indicators)+ 3个mutation(PUT execute, PUT verify, POST rollback)。 > 核心数据源:`analytics.store_task`、`analytics.store_task_log`、`analytics.indicator_dictionary`。 ### API: /tasks/:id (GET) | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 门店名 | `task.store_name` | `store_name` | store_task | | 问题指标 | `task.problem_indicator` | `problem_indicator` | 同上 | | 优先级 | `task.priority` | `priority` | 同上 | | 状态 | `task.status` | `status` | 同上 | | 当前值 | `task.current_value` | `current_value` | 同上 | | 基准值 | `task.benchmark_value` | `benchmark_value` | 同上 | | 目标值 | `task.target_value` | `target_value` | 同上 | | 问题描述 | `task.problem_description` | `problem_description` | 同上 | | 验收指标 | `task.verification_indicator` | `verification_indicator` | 同上 | | 行动要求 | `task.action_required` | `action_required` | 同上 | | 负责人 | `task.owner` | `owner` | 同上 | | 截止日 | `task.deadline?.substring(0,10)` | `deadline` | 同上 | | 过程证据 | `task.process_evidence` | `process_evidence` | 同上 | | 未完成原因 | `task.incomplete_reason` | `incomplete_reason` | 同上 | | 验收结果 | `task.verification_result` | `verification_result` | 同上 | | 操作日志 | `logs` | — | store_task_log | > 后端 `SELECT * FROM store_task WHERE task_id = $1` + `SELECT * FROM store_task_log WHERE task_id = $1`。 > `deadline?.substring(0, 10)` ✅,`log.created_at?.substring(0, 19)` ✅。 ### Mutations | Mutation | API | 说明 | |----------|-----|------| | executeMutation | PUT /tasks/:id/execute | 提交执行反馈,状态改为"进行中" | | verifyMutation | PUT /tasks/:id/verify | 月度验收,传 review_result/actual_value 等 | | rollbackMutation | POST /tasks/:id/rollback | 回滚任务 | > **指标说明**:从 `/tasks/indicators` 获取指标字典,匹配 `problem_indicator` 显示业务定义、计算公式、黄线/红线阈值。 --- ## 二十四、页面22:月度验收与复盘 MonthlyReviewPage > 7个Tab(验收汇总/升降级/任务完成率/活动清单/SKU治理/经验标准化/复盘优化),7个API(6 GET + 1 POST mutation),有MonthPicker。 > 核心数据源:`analytics.v_task_monthly_review`、`analytics.store_grade_change`、`analytics.store_task`、`analytics.bill_fact`、`analytics.mv_dish_sku_abc_monthly`、`analytics.standardized_practice`。 ### API总览 | API | Tab | 数据源表 | 说明 | |-----|-----|---------|------| | /tasks/monthly-review | 验收汇总 | v_task_monthly_review | 验收汇总+明细 | | /tasks/grade-change | 升降级 | store_grade_change | P0/P1/P2升降级 | | /tasks/monthly-review/completion | 完成率 | store_task | 按优先级汇总完成率 | | /tasks/monthly-review/activity-list | 活动清单 | bill_fact | 平台活动停/改/留 | | /tasks/monthly-review/sku-governance | SKU治理 | mv_dish_sku_abc_monthly | ABC分类+长尾清单 | | /tasks/practices | 经验标准化 | standardized_practice | 标准化经验列表 | | POST /tasks/practices | 经验标准化 | standardized_practice | 新增经验 | ### Tab1:验收汇总 #### API: /tasks/monthly-review | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 总任务数 | `summary.total` | — | 后端计算 result.rows.length | | 达标 | `summary.passed` | — | 后端 filter review_result='达标' | | 改善中 | `summary.improving` | — | 同上 | | 未改善 | `summary.failed` | — | 同上 | | 门店 | `r.store_name` | `store_name` | v_task_monthly_review | | 问题指标 | `r.problem_indicator` | `problem_indicator` | 同上 | | 基线 | `r.baseline_value` | `baseline_value` | 同上 | | 目标 | `r.target_value` | `target_value` | 同上 | | 实际 | `r.actual_value` | `actual_value` | 同上 | | 结果 | `r.review_result` | `review_result` | 同上 | > PieChart(验收结果分布)+ FilterableTable。后端 `SELECT * WHERE plan_month = $1`。 ### Tab2:升降级面板 #### API: /tasks/grade-change | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 月份 | `r.change_month?.substring(0,7)` | `change_month` | store_grade_change | | 门店 | `r.store_name` | `store_name` | 同上 | | 原级别 | `r.old_priority` | `old_priority` | 同上 | | 新级别 | `r.new_priority` | `new_priority` | 同上 | | 变化 | `r.change_type` | `change_type` | 同上 | | 原因 | `r.reason` | `reason` | 同上 | > `change_month?.substring(0, 7)` ✅。后端 `SELECT * WHERE change_month = $1::date`。 ### Tab3:任务完成率 #### API: /tasks/monthly-review/completion | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 优先级 | `c.priority` | `priority` | — | store_task | | 总数 | `c.total` | `total` | `count(*)` | 同上 | | 已完成 | `c.completed` | `completed` | `count FILTER(status='已验收'/'已回滚')` | 同上 | | 达标 | `c.passed` | `passed` | `count FILTER(verification_result='达标')` | 同上 | | 改善中 | `c.improving` | `improving` | 同上 | 同上 | | 未改善 | `c.failed` | `failed` | 同上 | 同上 | | 完成率 | `c.completion_rate` | `completion_rate` | `completed/total×100` | 同上 | > 进度条卡片 + FilterableTable。后端 `GROUP BY priority`。 ### Tab4:活动清单 #### API: /tasks/monthly-review/activity-list | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 活动方案 | `r.marketing_plan` | `marketing_plan` | — | bill_fact | | 账单数 | `r.bill_count` | `bill_count` | `count(*)` | 同上 | | 消费额 | `r.consumption` | `consumption` | `sum(consumption)` | 同上 | | 优惠额 | `r.discounts` | `discounts` | `sum(discount_total)` | 同上 | | 实收 | `r.received` | `received` | `sum(received_total)` | 同上 | | 优惠成本率 | `r.discount_cost_rate` | `discount_cost_rate` | `discounts/consumption×100` | — | | 净贡献 | `r.net_contribution` | `net_contribution` | `received - discounts` | — | | 建议 | 前端计算 | — | net>0 && rate<15→留, rate>30→停, else→改 | — | > FilterableTable,sortOptions=净贡献/实收/优惠成本率/账单数。 ### Tab5:SKU治理 #### API: /tasks/monthly-review/sku-governance | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | ABC分类 | `s.abc_class` | `abc_class` | mv_dish_sku_abc_monthly | | SKU数 | `s.sku_count` | `sku_count` | 同上 | | 营收占比 | `s.total_revenue_share` | `total_revenue_share` | 同上 | | 菜品名 | `r.dish_name` | `dish_name` | 同上(C类) | | 分类 | `r.category_level1` | `category_level1` | 同上 | | 账单数 | `r.bill_count` | `bill_count` | 同上 | | 实收 | `r.received_amount` | `received_amount` | 同上 | | 营收占比 | `r.revenue_share_pct` | `revenue_share_pct` | 同上 | > 后端返回 `{ summary, longtail }`,longtail `LIMIT 50`。 ### Tab6:经验标准化 #### API: /tasks/practices (GET + POST) | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 模块 | `p.practice_module` | `practice_module` | standardized_practice | | 标杆门店 | `p.benchmark_store_name` | `benchmark_store_name` | 同上 | | 关键动作 | `p.key_actions` | `key_actions` | 同上 | | 状态 | `p.status` | `status` | 同上 | > POST 新增经验,字段 `practice_module/benchmark_store_code/benchmark_store_name/key_actions/verification_indicators`。 ### Tab7:复盘优化 #### API: /tasks/monthly-review/indicator-effectiveness | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 指标 | `r.problem_indicator` | `problem_indicator` | — | store_task | | 任务数 | `r.task_count` | `task_count` | `count(*)` | 同上 | | 达标 | `r.passed` | `passed` | `count FILTER(verification_result='达标')` | 同上 | | 改善中 | `r.improving` | `improving` | 同上 | 同上 | | 未改善 | `r.failed` | `failed` | 同上 | 同上 | | 达标率 | `r.pass_rate` | `pass_rate` | `passed/task_count×100` | — | | 评价 | 前端计算 | — | ≥50%有效, ≥30%待观察, <30%低效 | — | > 后端 `GROUP BY problem_indicator ORDER BY pass_rate DESC`。 --- ## 二十五、页面23:数据质量看板 DataQualityPage > 单页面无Tab,1个API(GET),有MonthPicker。 > 核心数据源:`analytics.bill_fact`、`public.dish_sales_details`、`analytics.mv_inventory_cost_classified_monthly`。 ### API: /data-quality | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 总账单数 | `totalBills` | `total_bills` | `count(*)` | bill_fact | | 覆盖门店数 | `storeCount` | `store_count` | `count(DISTINCT store_code)` | 同上 | | 缺失账单号 | `missingBillNo` | `missing_bill_no` | `count FILTER(bill_no IS NULL/='')` | 同上 | | 缺失门店编码 | `missingStoreCode` | `missing_store_code` | 同上 | 同上 | | 零消费账单 | `zeroConsumption` | `zero_consumption` | `count FILTER(consumption=0/NULL)` | 同上 | | 负实收账单 | `negativeReceived` | `negative_received` | `count FILTER(received_total<0)` | 同上 | | 零实收账单 | `zeroReceived` | `zero_received` | `count FILTER(received_total=0/NULL)` | 同上 | | 优惠大于消费 | `discountGtConsumption` | `discount_gt_consumption` | `count FILTER(discount_total>consumption AND consumption>0)` | 同上 | | 日期范围 | `dq.min_date?.substring(0,10)` | `min_date/max_date` | `min/max(closed_at)` | 同上 | | 总菜品记录数 | `totalDishRecords` | `total_dish_records` | `count(*)` | dish_sales_details | | 缺失门店(菜品) | `dishMissingStore` | `dish_missing_store` | `count FILTER(store_code IS NULL/='')` | 同上 | | 缺失菜品名 | `dishMissingDish` | `dish_missing_dish` | `count FILTER(dish_name IS NULL/='')` | 同上 | | 负耗用记录数 | `negativeConsumptionCount` | `negative_consumption_count` | `count FILTER(consumption_amount<0)` | mv_inventory_cost_classified_monthly | | 负耗用金额 | `negativeConsumptionAmount` | `negative_consumption_amount` | `sum(consumption_amount) FILTER(<0)` | 同上 | > **注意**:账单和菜品统计无月份过滤(全量扫描),仅库存统计按 `month_start` 过滤。 > `min_date/max_date?.substring(0, 10)` ✅。 > **四层状态**:完整性/准确性/一致性/时效性,前端根据指标值计算。 > **置信等级**:A(正常)/B(待桥接)/C(异常)。 --- ## 二十六、页面24:银行授信数据报告 BankPage > 单页面无Tab,1个API(GET),有MonthPicker + 评分规则配置面板。 > 核心数据源:`analytics.mv_overview_monthly`、`analytics.mv_overview_daily`、`analytics.bill_fact`、`analytics.mv_store_operating_expense_monthly`、`analytics.mv_store_risk_rating_monthly`、`analytics.v_channel_daily`。 ### API: /bank/report 后端 `Promise.all` 并发6个查询,返回 `{ overview, daily, waterfall, risk, channel, stores, stability }`。 ### Block1:银行授信评估摘要 | 评分指标 | 前端变量 | 数据来源 | 计算公式 | |---------|---------|---------|---------| | 净利率 | `metrics.netMargin` | waterfall | `store_contribution/received×100` | | 营收稳定性 | `metrics.cv` | stability | `stdDev/meanDailyRevenue` | | 门店健康度 | `metrics.redPct` | risk | `红色门店数/总门店数×100` | | 规模体量 | `metrics.scale` | overview | `ov.received` | | 优惠率 | `metrics.discountRate` | overview | `ov.discount_rate_pct` | | 会员占比 | `metrics.memberShare` | overview | `ov.member_share_pct` | > **评分规则**:可配置权重和阈值,localStorage 持久化。单项得分 0-100,加权总分 → 信用等级 A(≥85)/B(≥70)/C(≥50)/D(<50)。 ### Block2:企业经营概况 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 营业收入 | `wf.consumption` | `waterfall.consumption` | bill_fact | | 优惠 | `wf.discount` | `waterfall.discount` | 同上 | | 月度实收 | `ov.received` | `overview.received` | mv_overview_monthly | | 客单价 | `ov.avg_bill_value` | `overview.avg_bill_value` | 同上 | | 理论毛利率 | `ov.theoretical_margin_pct` | `overview.theoretical_margin_pct` | 同上 | | 门店数量 | `stores.length` | — | mv_store_risk_rating_monthly | | 日实收趋势 | `dailyChart` | `daily` | mv_overview_daily | > `business_date?.substring(5, 10)` ✅。 ### Block3:盈利能力分析(利润瀑布) | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 营业收入 | `wf.consumption` | `consumption` | `sum(consumption)` | bill_fact | | 优惠 | `wf.discount` | `discount` | `sum(discount_total)` | 同上 | | 实收 | `wf.received` | `received` | `sum(received)` | mv_store_risk_rating_monthly | | 食材 | `wf.food_cost` | `food_cost` | `sum(actual_food_cost)` | mv_store_operating_expense_monthly | | 人工 | `wf.wage` | `wage` | `sum(wage_expense)` | 同上 | | 房租 | `wf.rent` | `rent` | `sum(rent_expense)` | 同上 | | 水电 | `wf.utility` | `utility` | `sum(utility_expense)` | 同上 | | 其他 | `wf.dorm+commission+other` | `dorm/commission/other_expense` | `sum(dorm+commission+card_fee+repair)` | 同上 | | 门店贡献利润 | `wf.store_contribution` | `store_contribution` | `received - food_cost - operating_expense` | — | > 自定义 BarChart shape 实现瀑布图效果。 ### Block4:门店资产质量 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 风险分布 | `riskPie` | `risk` | mv_store_risk_rating_monthly | | TOP10门店 | `top10` | `stores` | 同上 | > PieChart(风险等级分布)+ 横向 BarChart(TOP10门店实收)。 ### Block5:经营稳定性指标 | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 日度营收波动率 | `stability.cv` | `stability.cv` | `stdDev/mean` | mv_overview_daily | | 日均收入 | `stability.mean_daily_revenue` | `stability.mean_daily_revenue` | — | 同上 | | 优惠率 | `ov.discount_rate_pct` | `overview.discount_rate_pct` | — | mv_overview_monthly | | 会员消费占比 | `ov.member_share_pct` | `overview.member_share_pct` | — | 同上 | | 活跃渠道数 | `channelPie.length` | `channel` | 前端非零渠道数 | v_channel_daily | > 渠道 PieChart,标签显示渠道名称和占比。 --- ## 二十七、页面25:本体标准 OntologyPage > 5个Tab(概览/维度主数据/事实表/枚举标准/指标标准),5个API(全部GET),无MonthPicker。 > 核心数据源:`information_schema.columns`、`analytics.indicator_dictionary`、各 dim_/fact_/enum_ 表。 ### API总览 | API | Tab | 数据源 | 说明 | |-----|-----|--------|------| | /tasks/ontology/overview | 概览 | information_schema + indicator_dictionary | 表统计概览 | | /tasks/ontology/dim/:table | 维度主数据 | 对应 dim_ 表 | 维度表数据+字段定义 | | /tasks/ontology/fact/:table | 事实表 | 对应 fact_ 表 | 事实表字段定义 | | /tasks/ontology/enum/:table | 枚举标准 | 对应 enum_ 表 | 枚举值列表 | | /tasks/ontology/metrics | 指标标准 | indicator_dictionary | 指标字典 | ### Tab1:概览 > 显示维度表数、事实表数、枚举表数、指标标准数。点击可跳转到对应Tab。 ### Tab2-4:维度/事实/枚举 > 列表→详情模式。列表显示表名、字段数、行数。点击进入详情,FilterableTable 展示数据(pageSize=20),下方显示字段定义表。 > 后端使用正则校验表名 `^dim_\w+$` / `^fact_\w+$` / `^enum_\w+$` 防注入。 > 时间戳字段 `String(val).substring(0, 19)` ✅。 ### Tab5:指标标准 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 指标编码 | `r.metric_code` | `metric_code` | indicator_dictionary | | 指标名称 | `r.indicator_name` | `indicator_name` | 同上 | | 分类 | `r.metric_category` | `metric_category` | 同上 | | 业务定义 | `r.business_definition` | `business_definition` | 同上 | | 计算公式 | `r.formula` | `formula` | 同上 | | 单位 | `r.unit` | `unit` | 同上 | | 方向 | `r.direction` | `direction` | 同上 | | 负责人 | `r.owner` | `owner` | 同上 | | 黄线 | `r.yellow_threshold` | `yellow_threshold` | 同上 | | 红线 | `r.red_threshold` | `red_threshold` | 同上 | | 更新频率 | `r.update_frequency` | `update_frequency` | 同上 | | 版本 | `r.version` | `version` | 同上 | > 前端分类筛选(全部/收入/成本/优惠/会员/商品/库存/风险)+ FilterableTable。 --- ## 二十八、页面26:中央厨房成本驾驶舱 CentralKitchenPage > 单页面无Tab,1个API(GET),有MonthPicker。 > 核心数据源:`analytics.v_central_kitchen_product_full_cost`、`central_kitchen_processing_cost`、`central_kitchen_recipe_consumption`、`central_kitchen_manufacturing_cost_pool`、`central_kitchen_finished_receipt`。 ### API: /central-kitchen/dashboard 后端 `Promise.all` 并发7个查询,返回 `{ summary, reconciliation, products, categoryCost, recipeEfficiency, mfgPool, yieldAnalysis }`。 ### Block1:核心指标卡片 | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 产品数 | `summary.product_count` | `product_count` | `count(*)` | v_central_kitchen_product_full_cost | | 完工入库量 | `summary.total_inbound_qty` | `total_inbound_qty` | `sum(inbound_quantity)` | 同上 | | 理论材料成本 | `summary.theoretical_cost` | `theoretical_cost` | `sum(theoretical_cost)` | 同上 | | 实际材料成本 | `summary.material_actual_cost` | `material_actual_cost` | `sum(material_actual_cost)` | 同上 | | 完整制造成本 | `summary.full_manufacturing_cost` | `full_manufacturing_cost` | `sum(full_manufacturing_cost)` | 同上 | | 材料效率差异 | `summary.efficiency_variance_pct` | `efficiency_variance_pct` | `(实际-理论)/理论×100` | — | | 制造费用率 | `summary.mfg_cost_rate` | `mfg_cost_rate` | `制造费用池/实际材料×100` | — | > 后端 `WHERE report_month = $1::date`。 ### Block2:成本对账瀑布 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 理论成本 | `reconciliation.theoretical_cost` | `theoretical_cost` | v_central_kitchen_product_full_cost | | 标准成本 | `reconciliation.standard_cost` | `standard_cost` | 同上 | | 实际材料 | `reconciliation.material_actual_cost` | `material_actual_cost` | 同上 | | 制造费用池 | `reconciliation.manufacturing_cost_pool` | `manufacturing_cost_pool` | 同上 | | 全制造成本 | `reconciliation.full_manufacturing_cost` | `full_manufacturing_cost` | 同上 | | 入库价值 | `reconciliation.calculated_inbound_value` | `calculated_inbound_value` | central_kitchen_processing_cost | | 制造毛利 | `reconciliation.manufacturing_margin` | `manufacturing_margin` | 入库价值-全制造成本 | > 后端 JOIN `central_kitchen_processing_cost c ON p.report_month = c.report_month AND p.product_code = c.product_code`。 > 自定义 BarChart shape 实现瀑布图效果。 ### Block3:品类成本结构 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 品类 | `c.category_minor` | `category_minor` | v_central_kitchen_product_full_cost | | 产品数 | `c.product_count` | `product_count` | 同上 | | 入库量 | `c.total_qty` | `total_qty` | 同上 | | 理论成本 | `c.theoretical_cost` | `theoretical_cost` | 同上 | | 实际材料 | `c.material_actual_cost` | `material_actual_cost` | 同上 | | 制造费用 | `c.allocated_mfg_cost` | `allocated_mfg_cost` | 同上 | | 全成本 | `c.full_cost` | `full_cost` | 同上 | | 效率差异 | `c.efficiency_var_pct` | `efficiency_var_pct` | 同上 | > 堆叠 BarChart(材料成本+制造费用)+ FilterableTable。后端 `GROUP BY category_minor ORDER BY full_cost DESC`。 ### Block4:产品成本偏差散点图 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 产品名 | `p.product_name` | `product_name` | v_central_kitchen_product_full_cost | | 理论成本(X) | `p.theoretical_cost` | `theoretical_cost` | 同上 | | 实际成本(Y) | `p.material_actual_cost` | `material_actual_cost` | 同上 | | 偏差率(颜色) | `p.efficiency_variance_pct` | `efficiency_variance_pct` | 同上 | > ScatterChart,颜色按偏差率分4档:节约(≤-5%)/正常(-5%~0%)/轻微超耗(0~5%)/严重超耗(≥5%)。 ### Block5:产品成本明细表 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 编码 | `p.product_code` | `product_code` | v_central_kitchen_product_full_cost | | 产品名 | `p.product_name` | `product_name` | 同上 | | 品类 | `p.category_minor` | `category_minor` | 同上 | | 入库量 | `p.inbound_quantity` | `inbound_quantity` | 同上 | | 理论成本 | `p.theoretical_cost` | `theoretical_cost` | 同上 | | 标准成本 | `p.standard_cost` | `standard_cost` | 同上 | | 实际材料 | `p.material_actual_cost` | `material_actual_cost` | 同上 | | 制造费用 | `p.allocated_manufacturing_cost` | `allocated_manufacturing_cost` | 同上 | | 全成本 | `p.full_manufacturing_cost` | `full_manufacturing_cost` | 同上 | | 单位成本 | `p.full_unit_cost` | `full_unit_cost` | 同上 | | 效率差异 | `p.efficiency_variance_pct` | `efficiency_variance_pct` | 同上 | | 标准差异 | `p.standard_variance_pct` | `standard_variance_pct` | 同上 | | 入库价值 | `p.inbound_value` | `inbound_value` | central_kitchen_processing_cost | | 产品毛利 | `p.product_margin` | `product_margin` | 入库价值-全制造成本 | > FilterableTable,searchKeys=product_code/product_name/category_minor。后端 `ORDER BY p.full_manufacturing_cost DESC`。 ### Block6:配方效率分析 TOP20 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 配方 | `r.recipe_name` | `recipe_name` | central_kitchen_recipe_consumption | | 物料 | `r.item_name` | `item_name` | 同上 | | 规格 | `r.specification` | `specification` | 同上 | | 理论用量 | `r.theoretical_qty` | `theoretical_qty` | 同上 | | 实际用量 | `r.actual_qty` | `actual_qty` | 同上 | | 理论金额 | `r.theoretical_amt` | `theoretical_amt` | 同上 | | 实际金额 | `r.actual_amt` | `actual_amt` | 同上 | | 差异率 | `r.variance_pct` | `variance_pct` | 同上 | | 实际出成率 | `r.avg_actual_yield` | `avg_actual_yield` | 同上 | | 标准出成率 | `r.avg_recipe_yield` | `avg_recipe_yield` | 同上 | | 出成差异 | `r.yield_diff` | `yield_diff` | 同上 | > 后端 `WHERE business_date >= $1 AND business_date < ($1 + INTERVAL '1 month')`,`GROUP BY recipe_name, item_name, specification, unit`,`LIMIT 20`。 ### Block7:出成率分析 TOP20 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 配方 | `y.recipe_name` | `recipe_name` | central_kitchen_finished_receipt | | 产品名 | `y.product_name` | `product_name` | 同上 | | 理论入库量 | `y.theoretical_inbound_qty` | `theoretical_inbound_qty` | 同上 | | 实际入库量 | `y.actual_inbound_qty` | `actual_inbound_qty` | 同上 | | 达成率 | `y.avg_achievement_rate` | `avg_achievement_rate` | 同上 | | 预期偏差率 | `y.avg_expected_var_rate` | `avg_expected_var_rate` | 同上 | | 入库金额 | `y.total_inbound_amt` | `total_inbound_amt` | 同上 | | 退库量 | `y.total_return_qty` | `total_return_qty` | 同上 | | 退库率 | `y.return_rate` | `return_rate` | 同上 | > 后端 `WHERE receipt_date >= $1 AND receipt_date < ($1 + INTERVAL '1 month')`,`LIMIT 20`。 ### Block8:制造费用池明细 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 费用类型 | `m.cost_type` | `cost_type` | central_kitchen_manufacturing_cost_pool | | 费用子类 | `m.cost_subtype` | `cost_subtype` | 同上 | | 来源 | `m.source_type` | `source_type` | 同上 | | 原始金额 | `m.source_amount` | `source_amount` | 同上 | | 分摊比例 | `m.share_pct` | `share_pct` | 同上 | | 分摊金额 | `m.allocated_amount` | `allocated_amount` | 同上 | | 分摊方法 | `m.allocation_method` | `allocation_method` | 同上 | | 计入重建 | `m.include_in_rebuilt_cost` | `include_in_rebuilt_cost` | 同上 | | 临时 | `m.is_provisional` | `is_provisional` | 同上 | > 后端 `WHERE report_month = $1::date ORDER BY include_in_rebuilt_cost DESC, allocated_amount DESC`。 --- ## 二十九、页面27:配送—倒挤成本对账 DistributionReconciliationPage > 单页面无Tab,1个API(GET),有MonthPicker。 > 核心数据源:`mv_distribution_monthly`、`analytics.fact_inventory_snapshot`。 ### API: /distribution/reconciliation 后端先检查 `mv_distribution_monthly` 是否包含当月数据,如缺失则 `REFRESH MATERIALIZED VIEW CONCURRENTLY`。然后 `Promise.all` 并发5个查询,返回 `{ summary, storeReconciliation, topVariances, unmatchedItems, categoryReconciliation }`。 ### Block1:核心指标卡片 | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 对账品项行 | `summary.total_lines` | `total_lines` | `count(*)` | mv_distribution_monthly + fact_inventory_snapshot | | 匹配行数 | `summary.matched_lines` | `matched_lines` | `count FILTER(dist>0 AND consumption>0)` | 同上 | | 配送总额 | `summary.total_dist_amt` | `total_dist_amt` | `sum(dist_amt)` | mv_distribution_monthly | | 实际耗用 | `summary.total_consumption_amt` | `total_consumption_amt` | `sum(consumption_amt)` | fact_inventory_snapshot | | 倒挤应耗用 | `summary.reverse_consumption_amt` | `reverse_consumption_amt` | `期初+配送-期末` | — | | 差异金额 | `summary.variance_amt` | `variance_amt` | `实际耗用-倒挤应耗用` | — | | 差异率 | `summary.variance_pct` | `variance_pct` | `差异/实际耗用×100` | — | > 倒挤公式:应耗用 = 期初 + 配送 - 期末 | 差异 = 实际 - 应耗用。 ### Block2:倒挤成本对账瀑布 > 自定义 BarChart shape 实现瀑布图:期初库存→配送入库→应耗用(倒挤)→实际耗用→期末库存→差异金额。 ### Block3:品类维度对账 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 品类 | `c.minor_category` | `minor_category` | mv_distribution_monthly | | 品项数 | `c.item_count` | `item_count` | 同上 | | 配送量 | `c.dist_qty` | `dist_qty` | 同上 | | 配送金额 | `c.dist_amt` | `dist_amt` | 同上 | | 不含税成本 | `c.dist_cost_excl_tax` | `dist_cost_excl_tax` | 同上 | | 耗用金额 | `c.consumption_amt` | `consumption_amt` | fact_inventory_snapshot | | 期末库存 | `c.ending_amt` | `ending_amt` | 同上 | | 差异金额 | `c.variance_amt` | `variance_amt` | — | | 差异率 | `c.variance_pct` | `variance_pct` | — | > 分组 BarChart(配送金额/耗用金额/期末库存)+ FilterableTable。 > 后端使用 CTE:dist(按 minor_category 汇总配送)+ item_cat(品项分类映射)+ inv(库存按品项分类汇总),`FULL OUTER JOIN`。 ### Block4:门店差异散点图 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 门店名 | `s.store_name` | `store_name` | mv_distribution_monthly | | 配送金额(X) | `s.dist_amt` | `dist_amt` | 同上 | | 差异率(Y) | `s.variance_pct` | `variance_pct` | — | > ScatterChart,颜色按差异率分3档:正常(±3%)/关注(3~10%)/异常(>10%)。 ### Block5:门店对账明细 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 门店编码 | `s.store_code` | `store_code` | mv_distribution_monthly + fact_inventory_snapshot | | 门店名称 | `s.store_name` | `store_name` | mv_distribution_monthly | | 配送金额 | `s.dist_amt` | `dist_amt` | 同上 | | 不含税成本 | `s.dist_cost_excl_tax` | `dist_cost_excl_tax` | 同上 | | 期初库存 | `s.opening_amt` | `opening_amt` | fact_inventory_snapshot | | 实际耗用 | `s.consumption_amt` | `consumption_amt` | 同上 | | 期末库存 | `s.ending_amt` | `ending_amt` | 同上 | | 倒挤应耗用 | `s.reverse_consumption_amt` | `reverse_consumption_amt` | — | | 差异金额 | `s.variance_amt` | `variance_amt` | — | | 差异率 | `s.variance_pct` | `variance_pct` | — | | 负库存数 | `s.neg_inventory_count` | `neg_inventory_count` | fact_inventory_snapshot | > 后端 CTE:dist(按 store_code 汇总)+ inv(按 store_code 汇总,含 `count FILTER(is_negative)`)+ store_names,`FULL OUTER JOIN`。 > `ORDER BY abs(差异金额) DESC`。 ### Block6:Top30差异品项 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 门店 | `v.store_code` | `store_code` | mv_distribution_monthly | | 品项编码 | `v.item_code` | `item_code` | 同上 | | 品项名称 | `v.item_name` | `item_name` | 同上 | | 品类 | `v.minor_category` | `minor_category` | 同上 | | 配送量 | `v.dist_qty` | `dist_qty` | 同上 | | 配送金额 | `v.dist_amt` | `dist_amt` | 同上 | | 期初 | `v.opening_amt` | `opening_amt` | fact_inventory_snapshot | | 实际耗用 | `v.consumption_amt` | `consumption_amt` | 同上 | | 期末 | `v.ending_amt` | `ending_amt` | 同上 | | 倒挤应耗用 | `v.reverse_consumption_amt` | `reverse_consumption_amt` | — | | 差异金额 | `v.variance_amt` | `variance_amt` | — | | 差异率 | `v.variance_pct` | `variance_pct` | — | > 后端 CTE:dist(按 store_code+item_code 汇总,含 item_name/unit/category)+ inv,`LEFT JOIN`,`WHERE consumption_amt > 0`,`LIMIT 30`。 ### Block7:未匹配品项(有配送无库存耗用) | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 品项编码 | `u.item_code` | `item_code` | mv_distribution_monthly | | 品项名称 | `u.item_name` | `item_name` | 同上 | | 品类 | `u.minor_category` | `minor_category` | 同上 | | 配送量 | `u.dist_qty` | `dist_qty` | 同上 | | 配送金额 | `u.dist_amt` | `dist_amt` | 同上 | | 涉及门店数 | `u.store_count` | `store_count` | 同上 | > 后端 CTE:dist_items(按 item_code 汇总,含 `count(DISTINCT store_code)`)+ inv_items(库存中的 material_code),`WHERE d.item_code NOT IN (SELECT material_code FROM inv_items)`,`LIMIT 20`。 --- ## 三十、页面28:多级BOM成本穿透 BomPenetrationPage > 单页面无Tab,2个API(GET overview + GET product detail),有MonthPicker。 > 核心数据源:`central_kitchen_processing_cost`、`central_kitchen_recipe_consumption`、`analytics.fact_recipe_bom`、`analytics.dim_material`。 ### API总览 | API | 触发条件 | 数据源表 | 说明 | |-----|---------|---------|------| | /central-kitchen/bom-penetration | 页面加载 | central_kitchen_processing_cost + recipe_consumption | 概览:产品列表+BOM汇总+多级链 | | /central-kitchen/bom-penetration?productCode=xxx | 点击产品 | 同上 | BOM树递归展开 | ### Block1:核心指标卡片 | 显示指标 | 前端变量 | 计算公式 | 数据来源 | |---------|---------|---------|---------| | 完工产品数 | `totalProducts` | `productList.length` | /central-kitchen/bom-penetration | | 含多级BOM | `multiLevelProducts` | `productList.filter(has_multi_level_bom).length` | 同上 | | BOM理论成本 | `totalBomTheoretical` | `bomSummary.reduce(bom_theoretical_amt)` | 同上 | | BOM领用成本 | `totalBomIssue` | `bomSummary.reduce(bom_issue_amt)` | 同上 | | 差异金额 | `totalVariance` | `bomSummary.reduce(variance_amt)` | 同上 | | 半成品依赖链 | `multiLevelChains.length` | — | 同上 | ### Block2:BOM成本结构Top15 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 产品编码 | `b.product_code` | `product_code` | central_kitchen_processing_cost | | 产品名称 | `b.product_name` | `product_name` | 同上 | | 材料数 | `b.material_count` | `material_count` | central_kitchen_recipe_consumption | | 半成品数 | `b.sub_product_count` | `sub_product_count` | 同上 | | 理论成本 | `b.bom_theoretical_amt` | `bom_theoretical_amt` | 同上 | | 领用成本 | `b.bom_issue_amt` | `bom_issue_amt` | 同上 | | 单位理论成本 | `b.bom_unit_theoretical_cost` | `bom_unit_theoretical_cost` | — | | 单位领用成本 | `b.bom_unit_issue_cost` | `bom_unit_issue_cost` | — | | 差异金额 | `b.variance_amt` | `variance_amt` | — | | 差异率 | `b.variance_pct` | `variance_pct` | — | > 分组 BarChart(理论成本+领用成本)+ FilterableTable,点击行可打开BOM树弹窗。 > 后端 CTE:bom(JOIN recipe_consumption + LEFT JOIN processing_cost 判断半成品),`GROUP BY product_code, product_name, inbound_quantity`。 ### Block3:BOM成本分布Treemap > Treemap,size=bom_theoretical_amt,颜色按差异率分3档。 > 数据来源同 Block2 的 bomSummary。 ### Block4:产品列表 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 产品编码 | `p.product_code` | `product_code` | central_kitchen_processing_cost | | 产品名称 | `p.product_name` | `product_name` | 同上 | | 品类 | `p.category_minor` | `category_minor` | 同上 | | 入库量 | `p.inbound_quantity` | `inbound_quantity` | 同上 | | 理论成本 | `p.theoretical_cost` | `theoretical_cost` | 同上 | | 实际成本 | `p.actual_cost` | `actual_cost` | 同上 | | 材料数 | `p.material_count` | `material_count` | central_kitchen_recipe_consumption | | 多级BOM | `p.has_multi_level_bom` | `has_multi_level_bom` | — | > 后端 CTE:bom_materials(recipe_consumption 中的 material_code)+ finished_products(processing_cost 中的 product_code)+ multi_level(bom_materials JOIN finished_products),判断产品是否为半成品。 ### Block5:半成品依赖链 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 成品编码 | `c.finished_code` | `finished_code` | central_kitchen_processing_cost | | 成品名称 | `c.finished_name` | `finished_name` | 同上 | | 成品配方 | `c.finished_recipe` | `finished_recipe` | 同上 | | 半成品编码 | `c.sub_product_code` | `sub_product_code` | central_kitchen_recipe_consumption | | 半成品名称 | `c.sub_product_finished_name` | `sub_product_finished_name` | central_kitchen_processing_cost | | 半成品配方 | `c.sub_product_recipe` | `sub_product_recipe` | 同上 | > 后端 CTE:bom_materials + finished_products,`WHERE bm.material_code IN (SELECT product_code FROM finished_products)`。 ### Block6:BOM树弹窗(选中产品后) | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 物料名称 | `node.material_name` | `material_name` | central_kitchen_recipe_consumption | | 编码 | `node.material_code` | `material_code` | 同上 | | 单位 | `node.unit` | `unit` | 同上 | | 理论量 | `node.theoretical_qty` | `theoretical_qty` | 同上 | | 领用量 | `node.issue_qty` | `issue_qty` | 同上 | | 理论金额 | `node.theoretical_amt` | `theoretical_amt` | 同上 | | 领用金额 | `node.issue_amt` | `issue_amt` | 同上 | | 差异金额 | 前端计算 | — | `issue_amt - theoretical_amt` | > 后端使用 `WITH RECURSIVE bom_tree` 递归展开BOM,最大5层。 > Level 1:直接原材料(JOIN recipe_agg ON pc.recipe_name = ra.recipe_name)。 > Level 2+:递归展开半成品(`WHERE bt.is_finished_product AND bt.level < 5`)。 > 无 productCode 时 `LIMIT 200`,有 productCode 时按指定产品展开。 > 前端 `buildTree()` 将扁平数据构建为树结构,支持懒加载子节点。 --- ## 三十一、页面29:销量驱动生产与要货计划 ProductionPlanPage > 单页面无Tab,1个API(GET),有MonthPicker。 > 核心数据源:`mv_dish_sales_monthly`、`analytics.fact_recipe_bom`、`analytics.dim_sku`、`analytics.dim_material`、`mv_distribution_monthly`、`analytics.fact_inventory_snapshot`、`central_kitchen_processing_cost`、`inventory_store_mapping`。 ### API: /sales-driven/production-plan 后端使用单一连接(`pool.connect()`),先检查并刷新 `mv_dish_sales_monthly` 和 `mv_distribution_monthly` 物化视图,然后创建临时表 `tmp_sales_agg`(从物化视图导入销量数据并建索引),再 `Promise.all` 并发6个查询,返回 `{ summary, skuSales, materialDemand, storeDemand, ckProductionPlan, productionCoord }`。 ### Block1:核心指标卡片 | 显示指标 | 前端变量 | API字段 | 计算公式 | 数据源表 | |---------|---------|---------|---------|---------| | 总菜品数 | `summary.total_dish_count` | `total_dish_count` | `count(DISTINCT dish_name)` | mv_dish_sales_monthly | | 有BOM菜品 | `summary.matched_sku_count` | `matched_sku_count` | `count(*) JOIN fact_recipe_bom` | 同上 + fact_recipe_bom | | BOM覆盖率 | `summary.bom_coverage_pct` | `bom_coverage_pct` | `matched/total×100` | — | | BOM原料数 | `summary.total_bom_materials` | `total_bom_materials` | `count(DISTINCT material_code)` | fact_recipe_bom | | 匹配销售额 | `summary.matched_amt` | `matched_amt` | `sum(amt) JOIN bom` | mv_dish_sales_monthly + fact_recipe_bom | | 总销售额 | `summary.total_sales_amt` | `total_sales_amt` | `sum(amt)` | mv_dish_sales_monthly | | CK生产计划 | 前端计算 | — | `ckProductionPlan.filter(demand_qty>0).length` | — | ### Block2:BOM覆盖率饼图 + Top15原料需求 > PieChart(有BOM vs 无BOM菜品数)。 > BarChart(Top15原料需求量),数据来自 materialDemand。 ### Block3:中央厨房生产计划 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 产品编码 | `r.product_code` | `product_code` | central_kitchen_processing_cost | | 产品名称 | `r.product_name` | `product_name` | 同上 | | 需求量 | `r.demand_qty` | `demand_qty` | mv_dish_sales_monthly × fact_recipe_bom | | 实际入库 | `r.actual_inbound_qty` | `actual_inbound_qty` | central_kitchen_processing_cost | | 差异量 | `r.variance_qty` | `variance_qty` | 需求-实际入库 | | 差异率 | `r.variance_pct` | `variance_pct` | 差异/实际入库×100 | | 涉及门店 | `r.store_count` | `store_count` | — | | 实际成本 | `r.actual_cost` | `actual_cost` | central_kitchen_processing_cost | > 后端 CTE:ck_demand(销量×BOM 中 material_name IN processing_cost.product_name 的记录)+ ck_actual(processing_cost 按产品汇总),`LEFT JOIN` + `UNION ALL`(补全有实际无需求的产品)。 > **注意**:需求与实际通过 `material_name = product_name` 名称匹配,非编码匹配。 ### Block4:生产与配送协同 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 产品编码 | `r.product_code` | `product_code` | central_kitchen_processing_cost | | 产品名称 | `r.product_name` | `product_name` | 同上 | | 完工入库 | `r.inbound_qty` | `inbound_qty` | 同上 | | 实际成本 | `r.actual_cost` | `actual_cost` | 同上 | | 理论成本 | `r.theoretical_cost` | `theoretical_cost` | 同上 | | 配送量 | `r.dist_qty` | `dist_qty` | mv_distribution_monthly (DC=2) | | 配送金额 | `r.dist_amt` | `dist_amt` | 同上 | | 中央厨房出库 | `r.ck_consumption_qty` | `ck_consumption_qty` | fact_inventory_snapshot (store_code='2') | | 中央厨房期末 | `r.ck_ending_qty` | `ck_ending_qty` | 同上 | | 其他仓库消耗 | `r.other_wh_consumption_qty` | `other_wh_consumption_qty` | fact_inventory_snapshot (非门店非CK) | | 其他仓库期末 | `r.other_wh_ending_qty` | `other_wh_ending_qty` | 同上 | | 门店消耗 | `r.consumption_qty` | `consumption_qty` | fact_inventory_snapshot (门店) | | 门店期末库存 | `r.ending_qty` | `ending_qty` | 同上 | | 完工配送率 | `r.completion_distribution_rate` | `completion_distribution_rate` | 配送/完工×100 | | 配送消耗率 | `r.distribution_consumption_rate` | `distribution_consumption_rate` | 门店消耗/配送×100 | | 库存积压率 | `r.inventory_accumulation_rate` | `inventory_accumulation_rate` | 门店期末/门店消耗×100 | > 后端 CTE:ck_production + ck_distribution(DC=2) + ck_inventory(store=2) + other_wh_inventory + store_consumption,4个 `FULL OUTER JOIN`。 > 其他仓库定义:`inventory_store_mapping WHERE sales_store_code IS NULL OR = ''` 且 `store_code != '2'`。 > 门店定义:`store_code NOT IN (非门店的 cost_unit_code)`。 > `LIMIT 50`。 ### Block5:门店要货建议 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 门店编码 | `r.store_code` | `store_code` | mv_dish_sales_monthly | | 门店名称 | `r.store_name` | `store_name` | 同上 | | 有BOM菜品数 | `r.sku_count` | `sku_count` | 同上 + fact_recipe_bom | | 销量 | `r.total_qty` | `total_qty` | mv_dish_sales_monthly | | 销售额 | `r.total_amt` | `total_amt` | 同上 | | 原料需求量 | `r.total_material_demand_qty` | `total_material_demand_qty` | 同上 × fact_recipe_bom | | 期末库存量 | `r.ending_inventory_qty` | `ending_inventory_qty` | fact_inventory_snapshot | | 期末库存额 | `r.ending_inventory_amt` | `ending_inventory_amt` | 同上 | | 建议要货量 | `r.suggested_order_qty` | `suggested_order_qty` | 原料需求-期末库存 | > 后端 CTE:store_demand(按 store_code 汇总销量+BOM展开)+ inv(按 store_code 汇总期末库存),`LEFT JOIN`。 ### Block6:原料需求明细 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 原料编码 | `r.material_code` | `material_code` | fact_recipe_bom | | 原料名称 | `r.material_name` | `material_name` | dim_material | | 单位 | `r.unit` | `unit` | fact_recipe_bom | | 总需求量 | `r.total_demand_qty` | `total_demand_qty` | mv_dish_sales_monthly × fact_recipe_bom | | 总需求金额 | `r.total_demand_amt` | `total_demand_amt` | 同上(含配送单价) | | 涉及门店数 | `r.store_count` | `store_count` | — | | 门店均需求 | `r.avg_store_demand` | `avg_store_demand` | — | > 后端 CTE:demand(tmp_sales_agg JOIN fact_recipe_bom LEFT JOIN dim_material LEFT JOIN mv_distribution_monthly 获取单价),`LIMIT 100`。 > **注意**:原料单价通过 `m.material_name = ic.item_name` 名称匹配配送记录。 ### Block7:SKU销量Top50 | 显示指标 | 前端变量 | API字段 | 数据源表 | |---------|---------|---------|---------| | 菜品名称 | `r.dish_name` | `dish_name` | mv_dish_sales_monthly | | SKU编码 | `r.sku_code` | `sku_code` | dim_sku | | 销量 | `r.qty` | `qty` | mv_dish_sales_monthly | | 销售额 | `r.amt` | `amt` | 同上 | | 门店数 | `r.store_count` | `store_count` | 同上 | | 均价 | `r.avg_unit_price` | `avg_unit_price` | 同上 | | 有BOM | `r.has_bom` | `has_bom` | fact_recipe_bom | | 材料数 | `r.material_count` | `material_count` | 同上 | > 后端 CTE:sales(按 dish_name 汇总)LEFT JOIN dim_sku ON `s.dish_name = sk.standard_name` LEFT JOIN fact_recipe_bom,`LIMIT 50`。 > **注意**:SKU匹配通过 `dish_name = standard_name` 名称匹配。 ---