fix: 修复API数据正确性问题

- 移除3个API的operating_expense IS NOT NULL过滤,改用COALESCE
  - /overview/store-profit-ranking: 93家门店全部纳入排名
  - /overview/profit-opportunity: 所有有营收门店纳入机会计算
  - /overview/profit-trend: LEFT JOIN替代INNER JOIN
- 重建v_store_operating_expense_monthly视图,去除硬编码_april视图
  - sales CTE改用mv_store_site_profile_monthly
  - food CTE改用mv_store_theoretical_actual_cost_monthly
  - 支持任意月份查询
- 修复人工优化和能源优化opportunity为负数问题
- 新增scripts/refresh_materialized_views.sh自动化刷新脚本
- 新增本体.md/API.md/原始数据级导入处理.md/物化视图更新.md/API数据检查报告.md
This commit is contained in:
freedakgmail
2026-08-11 19:25:49 +08:00
parent 3f1c291486
commit a090fc7c19
7 changed files with 1677 additions and 19 deletions
+419
View File
@@ -0,0 +1,419 @@
# 后端 API 完整列表
## 路由挂载(server/src/index.ts
| 前缀 | 路由文件 | 说明 |
|------|----------|------|
| `/api/auth` | auth.ts | 认证 |
| `/api/admin` | admin.ts | 平台管理(租户管理) |
| `/api` | data.ts | 核心数据(overview/stores/cost/risk等) |
| `/api/tasks` | tasks.ts | 任务管理+本体查询 |
| `/api/cost-analysis` | cost-analysis.ts | 成本分析 |
| `/api/store-expense` | store-expense.ts | 门店费用 |
| `/api/smart-scheduling` | smart-scheduling.ts | 智能排班 |
| `/api/situational-awareness` | situational-awareness.ts | 态势感知 |
| `/api/analytics-enhanced` | analytics-enhanced.ts | 增强分析 |
| `/api/tts` | tts.ts | 语音合成 |
| `/api/target` | target.ts | 目标管理 |
| `/api/scheduler` | scheduler.ts | 调度管理 |
| `/api/alert` | alert.ts | 预警管理 |
| `/api/store-grade` | store-grade.ts | 门店分级 |
| `/api/product` | product.ts | 产品管理 |
| `/api/enterprise` | enterprise.ts | 企业管理 |
| `/api/intelligence` | intelligence.ts | 智能分析 |
| `/api/promotion` | promotion.ts | 促销分析 |
---
## 1. 认证 `/api/auth`
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/auth/login` | 用户登录 |
| GET | `/auth/me` | 获取当前用户信息 |
## 2. 平台管理 `/api/admin`
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/admin/login` | 平台管理员登录 |
| GET | `/admin/tenants` | 获取所有租户 |
| GET | `/admin/tenants/:tenantId` | 获取单个租户详情 |
| POST | `/admin/tenants` | 创建租户 |
| PUT | `/admin/tenants/:tenantId` | 更新租户 |
## 3. 核心数据 `/api`
| 方法 | 路径 | 说明 | 数据源 |
|------|------|------|--------|
| GET | `/overview` | 经营总览 | `mv_overview_monthly` + `bill_fact` |
| GET | `/overview/daily` | 日度总览 | `v_overview_daily` |
| GET | `/overview/profit-waterfall` | 利润瀑布 | `mv_store_risk_rating_monthly` + `mv_store_operating_expense_monthly` + `bill_fact` |
| GET | `/overview/store-profit-ranking` | 门店利润排名 | `mv_store_operating_expense_monthly` |
| GET | `/overview/profit-opportunity` | 利润机会池 | `mv_store_risk_rating_monthly` + `mv_store_operating_expense_monthly` + `mv_dish_sku_abc_monthly` |
| GET | `/overview/yoy` | 同比分析 | `mv_overview_monthly` |
| GET | `/overview/mom` | 环比分析 | `mv_overview_monthly` |
| GET | `/overview/trend` | 趋势分析 | `mv_overview_monthly` |
| GET | `/overview/profit-trend` | 利润趋势 | `mv_store_operating_expense_monthly` |
| GET | `/overview/period` | 时段分析 | `v_hourly_summary` |
| GET | `/stores` | 门店列表 | `mv_store_risk_rating_monthly` |
| GET | `/stores/risk` | 门店风险 | `mv_store_risk_rating_monthly` |
| GET | `/stores/priority` | 门店优先级 | `mv_store_action_priority_deep_monthly` |
| GET | `/stores/quadrant` | 门店象限 | `mv_store_risk_rating_monthly` |
| GET | `/stores/:code` | 门店详情 | `bill_records` (直接查原始表) |
| GET | `/stores/:code/daily` | 门店日度 | `bill_records` (直接查原始表) |
| GET | `/stores/:code/meal-period` | 门店餐段 | `bill_records` (直接查原始表) |
| GET | `/stores/:code/category-mix` | 门店品类结构 | `mv_store_category_mix_monthly` |
| GET | `/stores/:code/cost` | 门店成本 | `mv_store_theoretical_actual_cost_monthly` |
| GET | `/stores/:code/member` | 门店会员 | `v_store_member_monthly_activity` |
| GET | `/stores/:code/anomalies` | 门店异常 | `v_anomaly_bills` |
| GET | `/stores/:code/staffing` | 门店人效 | `attendance_records` + `bill_records` |
| GET | `/cost/comparison` | 成本对比 | `mv_store_theoretical_actual_cost_monthly` |
| GET | `/cost/category-benchmark` | 品类成本基准 | `mv_inventory_cost_classified_monthly` |
| GET | `/cost/inventory` | 库存成本 | `inventory_cost_records` |
| GET | `/platform/economics` | 平台经济 | `mv_store_platform_economics_monthly` |
| GET | `/member/comparison` | 会员对比 | `v_member_comparison` |
| GET | `/member/repeat` | 会员复购 | `v_member_repeat_summary_monthly` |
| GET | `/sku/abc` | SKU ABC分类 | `mv_dish_sku_abc_monthly` |
| GET | `/sku/category` | SKU品类 | `mv_dish_sku_summary_monthly` |
| GET | `/sku/attach` | SKU关联 | `mv_dish_basket_monthly` |
| GET | `/risk/anomaly` | 异常风险 | `v_anomaly_summary` |
| GET | `/risk/zero-received` | 零实收风险 | `v_zero_received_store_summary` |
| GET | `/risk/cashier` | 收银风险 | `v_cashier_risk` |
| GET | `/marketing/plans` | 营销计划 | `v_marketing_plan_summary` |
| GET | `/benchmark/composite` | 综合基准 | `mv_store_benchmark_composite_monthly` |
| GET | `/time/weekday` | 星期分析 | `v_weekday_summary` |
| GET | `/time/hourly` | 小时分析 | `v_hourly_summary` |
| GET | `/channel` | 渠道分析 | `v_channel_daily` |
| GET | `/data-quality` | 数据质量 | `v_data_quality_check` |
| GET | `/region/summary` | 区域汇总 | `v_region_summary` |
| GET | `/site-selection/profile` | 选址画像 | `mv_store_site_profile_monthly` |
| GET | `/site-selection/segment-benchmark` | 分段基准 | `mv_site_segment_benchmark_monthly` |
| GET | `/site-selection/replication` | 选址复制 | `mv_store_site_replication_monthly` |
| GET | `/site-selection/overlap-risk` | 重叠风险 | `mv_store_overlap_risk_monthly` |
| GET | `/site-selection/district-benchmark` | 区域基准 | `mv_district_site_benchmark_monthly` |
| GET | `/revenue/channel` | 渠道营收 | `v_channel_daily` |
| GET | `/revenue/meal-period` | 餐段营收 | `v_meal_period_daily` |
| GET | `/revenue/store-ranking` | 门店营收排名 | `mv_store_risk_rating_monthly` |
| GET | `/revenue/daily-summary` | 日度营收汇总 | `mv_daily_revenue` |
| GET | `/bank/report` | 银行报表 | `mv_store_risk_rating_monthly` + `mv_store_operating_expense_monthly` + `bill_fact` |
| GET | `/central-kitchen/dashboard` | 中央厨房总览 | `central_kitchen_*` 系列表 |
| GET | `/central-kitchen/bom-penetration` | BOM穿透 | `central_kitchen_*` 系列表 |
| GET | `/distribution/reconciliation` | 配送对账 | `distribution_detail_records` |
| GET | `/sales-driven/production-plan` | 销售驱动生产计划 | `bill_fact` + `fact_recipe_bom` |
## 4. 任务管理 `/api/tasks`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/tasks` | 任务列表 |
| POST | `/tasks` | 创建任务 |
| POST | `/tasks/auto-generate` | 自动生成任务 |
| GET | `/tasks/weekly-check` | 周度检查 |
| GET | `/tasks/monthly-review` | 月度复盘 |
| GET | `/tasks/monthly-review/completion` | 月度复盘完成度 |
| GET | `/tasks/monthly-review/activity-list` | 月度复盘活动列表 |
| GET | `/tasks/monthly-review/sku-governance` | SKU治理 |
| GET | `/tasks/monthly-review/indicator-effectiveness` | 指标有效性 |
| GET | `/tasks/followup` | 跟进任务 |
| GET | `/tasks/grade-change` | 分级变更 |
| GET | `/tasks/practices` | 最佳实践列表 |
| POST | `/tasks/practices` | 创建最佳实践 |
| GET | `/tasks/practices/:id/replication-result` | 实践复制结果 |
| POST | `/tasks/practices/:id/replicate` | 复制实践 |
| POST | `/tasks/practices/:id/promote` | 推广实践 |
| GET | `/tasks/indicators` | 指标列表 |
| POST | `/tasks/indicators` | 创建指标 |
| PUT | `/tasks/indicators/:id/threshold` | 更新指标阈值 |
| GET | `/tasks/loop-health` | 闭环健康度 |
| POST | `/tasks/notifications/dispatch` | 通知派发 |
| GET | `/tasks/stores/:code/daily-card` | 门店日度卡片 |
| GET | `/tasks/:id` | 任务详情 |
| PUT | `/tasks/:id` | 更新任务 |
| PUT | `/tasks/:id/execute` | 执行任务 |
| PUT | `/tasks/:id/weekly-check` | 周度检查 |
| PUT | `/tasks/:id/verify` | 验证任务 |
| POST | `/tasks/:id/rollback` | 回滚任务 |
| GET | `/tasks/ontology/overview` | 本体总览 |
| GET | `/tasks/ontology/dim/:table` | 维度表查询 |
| GET | `/tasks/ontology/fact/:table` | 事实表查询 |
| GET | `/tasks/ontology/enum/:table` | 枚举表查询 |
| GET | `/tasks/ontology/metrics` | 指标查询 |
## 5. 成本分析 `/api/cost-analysis`
| 方法 | 路径 | 说明 | 数据源 |
|------|------|------|--------|
| GET | `/cost-analysis/overview` | 成本总览 | `dish_cost_analysis_summary` |
| GET | `/cost-analysis/category-comparison` | 品类成本对比 | `dish_cost_analysis_summary` |
| GET | `/cost-analysis/margin-deviation` | 毛利率偏差 | `dish_cost_analysis_summary` |
| GET | `/cost-analysis/variance-top` | 成本差异TOP | `dish_cost_analysis_summary` |
| GET | `/cost-analysis/menu-engineering` | 菜单工程 | `dish_cost_analysis_summary` |
| GET | `/cost-analysis/profitability` | 盈利能力 | `dish_cost_analysis_summary` |
| GET | `/cost-analysis/pricing` | 定价分析 | `dish_cost_analysis_summary` |
| GET | `/cost-analysis/material-variance` | 原料差异 | `dish_cost_analysis_material_detail` |
| GET | `/cost-analysis/loss-distribution` | 损耗分布 | `dish_cost_analysis_material_detail` |
| GET | `/cost-analysis/material-loss-top` | 原料损耗TOP | `dish_cost_analysis_material_detail` |
| GET | `/cost-analysis/material-type-loss` | 原料类型损耗 | `dish_cost_analysis_material_detail` |
| GET | `/cost-analysis/bom-overview` | BOM总览 | `fact_recipe_bom` |
| GET | `/cost-analysis/bom-complexity` | BOM复杂度 | `fact_recipe_bom` |
| GET | `/cost-analysis/bom-high-loss` | BOM高损耗 | `fact_recipe_bom` |
| GET | `/cost-analysis/bom-missing` | BOM缺失 | `dish_cost_analysis_summary` |
| GET | `/cost-analysis/bom-composition` | BOM组成 | `fact_recipe_bom` |
| GET | `/cost-analysis/material-sharing` | 原料共享 | `fact_recipe_bom` |
| GET | `/cost-analysis/unique-material-risk` | 独有原料风险 | `fact_recipe_bom` |
| POST | `/cost-analysis/sku-simplify-simulate` | SKU精简模拟 | `fact_recipe_bom` |
| GET | `/cost-analysis/material-demand` | 原料需求 | `fact_recipe_bom` |
| GET | `/cost-analysis/packaging-overview` | 包装总览 | `dish_cost_analysis_summary` |
| GET | `/cost-analysis/packaging-detail` | 包装明细 | `dish_cost_analysis_material_detail` |
| GET | `/cost-analysis/data-quality` | 数据质量 | `dish_cost_analysis_summary` |
| GET | `/cost-analysis/unmatched-dishes` | 未匹配菜品 | `dish_cost_analysis_summary` |
| GET | `/cost-analysis/unmatched-materials` | 未匹配原料 | `dish_cost_analysis_material_detail` |
| GET | `/cost-analysis/store-overview` | 门店成本总览 | `mv_store_theoretical_actual_cost_monthly` |
| GET | `/cost-analysis/store-map` | 门店成本地图 | `mv_store_theoretical_actual_cost_monthly` |
| GET | `/cost-analysis/store-ranking` | 门店成本排名 | `mv_store_theoretical_actual_cost_monthly` |
| GET | `/cost-analysis/scatter` | 散点图数据 | `mv_store_theoretical_actual_cost_monthly` |
| POST | `/cost-analysis/diagnosis/generate` | 生成诊断 | `mv_store_theoretical_actual_cost_monthly` |
| GET | `/cost-analysis/diagnosis` | 诊断列表 | `dish_diagnosis_snapshot` |
| GET | `/cost-analysis/adjustment` | 调整列表 | `dish_adjustment_log` |
| POST | `/cost-analysis/adjustment` | 创建调整 | `dish_adjustment_log` |
| PUT | `/cost-analysis/adjustment/:id` | 更新调整 | `dish_adjustment_log` |
| GET | `/cost-analysis/adjustment/:id/verify` | 验证调整 | `dish_adjustment_result` |
| POST | `/cost-analysis/adjustment/:id/result` | 调整结果 | `dish_adjustment_result` |
## 6. 门店费用 `/api/store-expense`
| 方法 | 路径 | 说明 | 数据源 |
|------|------|------|--------|
| GET | `/store-expense/overview` | 费用总览 | `mv_store_operating_expense_monthly` |
| GET | `/store-expense/expense-structure` | 费用结构 | `v_operating_expense_account_monthly` |
| GET | `/store-expense/store-ranking` | 门店费用排名 | `mv_store_operating_expense_monthly` |
| GET | `/store-expense/store-contribution` | 门店贡献利润 | `mv_store_operating_expense_monthly` |
| GET | `/store-expense/rent-risk` | 房租风险 | `mv_store_operating_expense_monthly` |
| GET | `/store-expense/delivery-commission` | 外卖佣金 | `mv_store_operating_expense_monthly` |
| GET | `/store-expense/efficiency` | 人效坪效 | `mv_store_operating_expense_monthly` |
| GET | `/store-expense/fixed-variable` | 固定/变动费用 | `mv_store_operating_expense_monthly` |
| GET | `/store-expense/break-even` | 盈亏平衡 | `mv_store_operating_expense_monthly` |
| GET | `/store-expense/loss-diagnosis` | 亏损诊断 | `mv_store_operating_expense_monthly` |
| GET | `/store-expense/store-evaluation` | 门店评估 | `mv_store_operating_expense_monthly` |
| GET | `/store-expense/data-quality` | 费用数据质量 | `v_operating_expense_data_quality` |
## 7. 智能排班 `/api/smart-scheduling`
| 方法 | 路径 | 说明 | 数据源 |
|------|------|------|--------|
| GET | `/smart-scheduling/traffic-heatmap` | 流量热力图 | `bill_records` |
| GET | `/smart-scheduling/meal-period-traffic` | 餐段流量 | `bill_records` |
| GET | `/smart-scheduling/traffic-heatmap-staffing` | 流量+排班 | `bill_records` + `attendance_records` |
| GET | `/smart-scheduling/dow-traffic` | 星期流量 | `bill_records` |
| GET | `/smart-scheduling/traffic-overview` | 流量总览 | `bill_records` |
| GET | `/smart-scheduling/staffing-match` | 排班匹配 | `bill_records` + `attendance_records` |
| GET | `/smart-scheduling/stores` | 门店列表 | `bill_records` |
| GET | `/smart-scheduling/efficiency-ranking` | 人效排名 | `bill_records` + `attendance_records` |
| GET | `/smart-scheduling/position-distribution` | 岗位分布 | `attendance_records` |
| GET | `/smart-scheduling/scheduling-suggestion` | 排班建议 | `bill_records` + `attendance_records` |
| GET | `/smart-scheduling/attendance-alert` | 考勤预警 | `attendance_records` |
| GET | `/smart-scheduling/attendance-summary` | 考勤汇总 | `attendance_records` |
| GET | `/smart-scheduling/employee-analysis` | 员工分析 | `attendance_records` |
| GET | `/smart-scheduling/position-salary-compare` | 岗位薪资对比 | `salary_detail_records` |
| GET | `/smart-scheduling/turnover-stats` | 流失率统计 | `attendance_records` |
| GET | `/smart-scheduling/overall-analysis` | 综合分析 | `bill_records` + `attendance_records` |
| GET | `/smart-scheduling/staffing-forecast` | 排班预测 | `bill_records` + `attendance_records` |
## 8. 态势感知 `/api/situational-awareness`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/situational-awareness/health-score` | 健康度评分 |
| GET | `/situational-awareness/alerts` | 预警列表 |
| GET | `/situational-awareness/correlation` | 关联分析 |
| GET | `/situational-awareness/forecast` | 预测分析 |
## 9. 增强分析 `/api/analytics-enhanced`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/analytics-enhanced/member/ltv` | 会员LTV |
| GET | `/analytics-enhanced/menu-engineering/actions` | 菜单工程行动 |
| GET | `/analytics-enhanced/dish-pair/recommendations` | 菜品搭配推荐 |
| GET | `/analytics-enhanced/region/comparison` | 区域对比 |
| GET | `/analytics-enhanced/region/kpi-forecast` | 区域KPI预测 |
| GET | `/analytics-enhanced/store/daily-target` | 门店日度目标 |
| GET | `/analytics-enhanced/store/member-activity` | 门店会员活动 |
| GET | `/analytics-enhanced/employee/performance` | 员工绩效 |
| GET | `/analytics-enhanced/inventory/turnover` | 库存周转 |
| GET | `/analytics-enhanced/inventory/near-expiry` | 临期库存 |
| GET | `/analytics-enhanced/marketing/roi` | 营销ROI |
| GET | `/analytics-enhanced/region/inspection-plan` | 区域巡检计划 |
| GET | `/analytics-enhanced/kpi` | KPI达成率 |
## 10. 语音合成 `/api/tts`
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/tts` | 文本转语音 |
## 11. 目标管理 `/api/target`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/target/annual` | 年度目标 |
| POST | `/target/annual` | 创建年度目标 |
| DELETE | `/target/annual/:id` | 删除年度目标 |
| GET | `/target/regional` | 区域目标 |
| POST | `/target/regional` | 创建区域目标 |
| GET | `/target/store-monthly` | 门店月度目标 |
| POST | `/target/store-monthly` | 创建门店月度目标 |
| GET | `/target/daily` | 日度目标 |
| GET | `/target/personal-tasks` | 个人任务 |
| POST | `/target/decompose/annual-to-store` | 年度目标分解到门店 |
| POST | `/target/decompose/monthly-to-daily` | 月度目标分解到日 |
| GET | `/target/achievement` | 目标达成率 |
## 12. 调度管理 `/api/scheduler`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/scheduler` | 调度列表 |
| PATCH | `/scheduler/:id` | 更新调度 |
| POST | `/scheduler/:id/trigger` | 手动触发 |
## 13. 预警管理 `/api/alert`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/alert/rules` | 预警规则列表 |
| POST | `/alert/rules` | 创建预警规则 |
| PATCH | `/alert/rules/:id` | 更新预警规则 |
| DELETE | `/alert/rules/:id` | 删除预警规则 |
| GET | `/alert/logs` | 预警日志 |
| PATCH | `/alert/logs/:id` | 更新预警日志 |
| GET | `/alert/overview` | 预警总览 |
## 14. 门店分级 `/api/store-grade`
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/store-grade/auto-grade` | 自动分级 |
| GET | `/store-grade/grades` | 分级列表 |
| GET | `/store-grade/traffic-light` | 红黄绿灯 |
| GET | `/store-grade/closure-analysis` | 关停测算 |
## 15. 产品管理 `/api/product`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/product/products` | 产品列表 |
| POST | `/product/products` | 创建产品 |
| POST | `/product/products/:id/track` | 跟踪产品 |
| GET | `/product/reviews` | 评价列表 |
| POST | `/product/reviews` | 创建评价 |
| GET | `/product/reviews/overview` | 评价总览 |
| GET | `/product/import-logs` | 导入日志 |
| POST | `/product/import/trigger` | 触发导入 |
| GET | `/product/quality-rules` | 质量规则 |
| GET | `/product/inventory-snapshot` | 库存快照 |
| GET | `/product/reports` | 报告 |
| POST | `/product/reports/generate` | 生成报告 |
| GET | `/product/variance` | 差异 |
| POST | `/product/variance/generate` | 生成差异 |
| GET | `/product/budget-locks` | 预算锁 |
| POST | `/product/budget-locks` | 创建预算锁 |
| PATCH | `/product/budget-locks/:id/approve` | 审批预算锁 |
## 16. 企业管理 `/api/enterprise`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/enterprise/suppliers` | 供应商列表 |
| POST | `/enterprise/suppliers` | 创建供应商 |
| POST | `/enterprise/suppliers/:code/score` | 供应商评分 |
| GET | `/enterprise/purchase-orders` | 采购订单 |
| POST | `/enterprise/purchase-orders` | 创建采购订单 |
| GET | `/enterprise/price-comparison` | 价格对比 |
| GET | `/enterprise/approvals` | 审批列表 |
| POST | `/enterprise/approvals` | 创建审批 |
| POST | `/enterprise/approvals/:id/approve` | 审批通过 |
| GET | `/enterprise/approvals/:id/steps` | 审批步骤 |
| GET | `/enterprise/notifications` | 通知列表 |
| PATCH | `/enterprise/notifications/:id/read` | 标记已读 |
| POST | `/enterprise/notifications` | 创建通知 |
| GET | `/enterprise/hr/attendance` | 考勤 |
| GET | `/enterprise/hr/talent-matrix` | 人才矩阵 |
| GET | `/enterprise/training/courses` | 培训课程 |
| POST | `/enterprise/training/courses` | 创建课程 |
| GET | `/enterprise/training/records` | 培训记录 |
| POST | `/enterprise/training/records` | 创建记录 |
| GET | `/enterprise/campaigns` | 活动列表 |
| POST | `/enterprise/campaigns` | 创建活动 |
| GET | `/enterprise/coupons` | 优惠券 |
| POST | `/enterprise/coupons` | 创建优惠券 |
| GET | `/enterprise/budgets` | 预算 |
| POST | `/enterprise/budgets` | 创建预算 |
| POST | `/enterprise/scan/production` | 扫描生产 |
| POST | `/enterprise/scan/receiving` | 扫描收货 |
| POST | `/enterprise/scan/inspection` | 扫描巡检 |
| GET | `/enterprise/loop-verification` | 闭环验证 |
| POST | `/enterprise/loop-verification/check` | 闭环检查 |
| GET | `/enterprise/audit-logs` | 审计日志 |
| POST | `/enterprise/audit-logs` | 创建审计 |
| POST | `/enterprise/pii/mask` | PII脱敏 |
| GET | `/enterprise/anonymous-ranking` | 匿名排名 |
| GET | `/enterprise/supplier-benchmark` | 供应商基准 |
| GET | `/enterprise/supplier-benchmark/:skuCode` | 供应商基准详情 |
## 17. 智能分析 `/api/intelligence`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/intelligence/iot/devices` | IoT设备 |
| POST | `/intelligence/iot/devices` | 创建IoT设备 |
| POST | `/intelligence/iot/reading` | IoT读数 |
| GET | `/intelligence/iot/temperature-history` | 温度历史 |
| POST | `/intelligence/ai/adjust-target` | AI目标调整 |
| GET | `/intelligence/ai/forecasts` | AI预测 |
| POST | `/intelligence/ai/forecast` | 创建预测 |
| POST | `/intelligence/ai/quarterly-report` | 季报生成 |
| GET | `/intelligence/chain/production-sales` | 产销分析 |
| POST | `/intelligence/chain/mrp` | MRP计算 |
| GET | `/intelligence/realtime/dashboard` | 实时看板 |
| GET | `/intelligence/strategy/roi` | ROI分析 |
| POST | `/intelligence/strategy/roi/calculate` | ROI计算 |
| GET | `/intelligence/strategy/brand-assets` | 品牌资产 |
| POST | `/intelligence/strategy/brand-assets` | 创建品牌资产 |
| POST | `/intelligence/training/lock-scheduling` | 培训锁定排班 |
## 18. 促销分析 `/api/promotion`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/promotion/overview` | 促销总览 |
| GET | `/promotion/by-plan` | 按方案对比 |
| GET | `/promotion/by-store` | 按门店效果 |
| GET | `/promotion/daily-trend` | 促销日趋势 |
---
## API 统计
| 路由文件 | 端点数 |
|----------|--------|
| data.ts | 57 |
| tasks.ts | 31 |
| cost-analysis.ts | 35 |
| store-expense.ts | 12 |
| smart-scheduling.ts | 17 |
| situational-awareness.ts | 4 |
| analytics-enhanced.ts | 13 |
| enterprise.ts | 35 |
| product.ts | 15 |
| intelligence.ts | 16 |
| promotion.ts | 4 |
| target.ts | 12 |
| alert.ts | 7 |
| store-grade.ts | 4 |
| admin.ts | 5 |
| auth.ts | 2 |
| tts.ts | 1 |
| scheduler.ts | 3 |
| **合计** | **~273** |
+202
View File
@@ -0,0 +1,202 @@
# 后端 API 数据正确性检查报告
## 检查时间: 2026-08-11
---
## 一、问题汇总
| 严重度 | 问题 | 影响API | 状态 |
|--------|------|---------|------|
| **高** | `v_store_operating_expense_monthly` 视图硬编码 `_april` 视图 | 所有费用相关API | 未修复 |
| **高** | `mv_store_operating_expense_monthly` 物化表未随数据导入刷新 | 所有 `/store-expense/*` API | 需手动刷新 |
| **中** | 部分API直接查 `bill_records` 而非 `bill_fact` | 6个API | 设计如此 |
| **中** | `e.operating_expense IS NOT NULL` 过滤排除无费用门店 | 3个API | 部分已修复 |
| **低** | 固定月份物化视图 `*_april` 不支持其他月份 | 间接影响 | 待重构 |
---
## 二、问题详细分析
### 问题1: 视图硬编码 `_april`(严重)
**位置**: `v_store_operating_expense_monthly` 视图定义
**问题**: 该视图的 `sales` CTE 引用 `v_store_site_profile_april``food` CTE 引用 `v_store_theoretical_actual_cost_april`,这些是4月专用视图,硬编码了 `'2026-04-01'::date`
**影响**:
- 当查询 `report_month = '2026-05-01'` 时,sales 和 food 数据仍来自4月
- 所有通过 `v_store_operating_expense_monthly` 的数据都只有4月数据正确
- `mv_store_operating_expense_monthly` 物化表从此视图写入,也受影响
**受影响API**:
- `/api/store-expense/overview`
- `/api/store-expense/expense-structure`(直接查 `v_operating_expense_account_monthly`,不受影响)
- `/api/store-expense/store-ranking`
- `/api/store-expense/store-contribution`
- `/api/store-expense/rent-risk`
- `/api/store-expense/delivery-commission`
- `/api/store-expense/efficiency`
- `/api/store-expense/fixed-variable`
- `/api/store-expense/break-even`
- `/api/store-expense/loss-diagnosis`
- `/api/store-expense/store-evaluation`
- `/api/overview/profit-waterfall`
- `/api/overview/store-profit-ranking`
- `/api/overview/profit-opportunity`
- `/api/overview/profit-trend`
- `/api/bank/report`
**修复方案**: 将 `v_store_operating_expense_monthly` 视图中的 `_april` 引用改为通用月份查询,或使用 `mv_store_site_profile_monthly``mv_store_theoretical_actual_cost_monthly`
### 问题2: 物化表未刷新(严重)
**位置**: `mv_store_operating_expense_monthly`(物化表,非物化视图)
**问题**: 费用数据导入后,物化表未同步更新,需要手动 DELETE + INSERT。
**当前状态**: 已手动刷新2026年4月数据。
**受影响API**: 同问题1中的所有 `/store-expense/*` API
**修复方案**: 每次费用数据导入后执行:
```sql
DELETE FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '目标月份';
INSERT INTO analytics.mv_store_operating_expense_monthly
SELECT * FROM analytics.v_store_operating_expense_monthly WHERE report_month = '目标月份';
```
### 问题3: 部分API直接查 bill_records(中)
**设计说明**: 以下API直接查 `bill_records` 原始表而非 `bill_fact` 物化视图:
| API | 文件:行 | 说明 |
|-----|---------|------|
| `GET /stores/:code` | data.ts:184 | 门店详情(按c002门店编码查询) |
| `GET /stores/:code/daily` | data.ts:251 | 门店日度(按c003门店名称查询) |
| `GET /smart-scheduling/traffic-heatmap` | smart-scheduling.ts:165 | 流量热力图 |
| `GET /smart-scheduling/meal-period-traffic` | smart-scheduling.ts:293 | 餐段流量 |
| `GET /smart-scheduling/stores` | smart-scheduling.ts:325 | 门店列表 |
| `GET /smart-scheduling/dow-traffic` | smart-scheduling.ts:456 | 星期流量 |
| `GET /tasks/stores/:code/daily-card` | tasks.ts:311 | 门店日度卡片 |
| `GET /situational-awareness/forecast` | situational-awareness.ts:427 | 预测分析 |
**影响**: 这些API能实时反映 `bill_records` 的新数据,不受物化视图刷新影响。但缺点是查询性能较差(199列表无索引优化),且数据未标准化。
**结论**: 当前设计可接受,这些API需要实时数据且查询量不大。
### 问题4: `operating_expense IS NOT NULL` 过滤(中)
**已修复的API**:
- `/api/overview/profit-waterfall` — 已移除过滤,改用 `COALESCE(..., 0)`
- `/api/store-expense/overview` — 已移除过滤
**仍存在过滤的API**:
| API | 文件:行 | 过滤条件 | 影响 |
|-----|---------|----------|------|
| `GET /overview/store-profit-ranking` | data.ts:1104 | `e.operating_expense IS NOT NULL` | 无费用门店不参与排名 |
| `GET /overview/profit-opportunity` | data.ts:1132 | `e.operating_expense IS NOT NULL` | 无费用门店不参与机会计算 |
| `GET /overview/profit-trend` | data.ts:1440 | `e.operating_expense IS NOT NULL` | 无费用门店不参与趋势 |
**修复方案**: 将 `WHERE e.operating_expense IS NOT NULL` 改为 `LEFT JOIN` + `COALESCE`,确保所有有营收的门店都纳入分析。
### 问题5: 固定月份物化视图(低)
**问题**: 以下物化视图硬编码2026年4月:
- `mv_store_theoretical_actual_cost_april`
- `mv_store_site_profile_april`
- `mv_store_action_priority_deep_april`
- `dish_*_april` (6个)
**影响**: 这些视图被 `v_store_operating_expense_monthly` 引用,限制了多月份支持。
**修复方案**: 使用对应的 `_monthly` 通用物化视图替代。
---
## 三、数据源对照表
### 查询 `bill_fact`(物化视图,需REFRESH
| API | 数据源 |
|-----|--------|
| `/overview` | `mv_overview_monthly` + `bill_fact` |
| `/overview/profit-waterfall` | `mv_store_risk_rating_monthly` + `mv_store_operating_expense_monthly` + `bill_fact` |
| `/overview/profit-opportunity` | `mv_store_risk_rating_monthly` + `mv_store_operating_expense_monthly` + `mv_dish_sku_abc_monthly` |
| `/revenue/daily-summary` | `mv_daily_revenue` |
| `/stores/risk` | `mv_store_risk_rating_monthly` |
| `/stores/priority` | `mv_store_action_priority_deep_monthly` |
| `/cost/comparison` | `mv_store_theoretical_actual_cost_monthly` |
| `/cost-analysis/store-overview` | `mv_store_theoretical_actual_cost_monthly` |
| `/cost-analysis/store-ranking` | `mv_store_theoretical_actual_cost_monthly` |
### 查询 `bill_records`(原始表,实时)
| API | 数据源 |
|-----|--------|
| `/stores/:code` | `bill_records` |
| `/stores/:code/daily` | `bill_records` |
| `/smart-scheduling/*` (4个) | `bill_records` |
| `/tasks/stores/:code/daily-card` | `bill_records` |
| `/situational-awareness/forecast` | `bill_records` |
### 查询 `mv_store_operating_expense_monthly`(物化表,需手动更新)
| API | 数据源 |
|-----|--------|
| `/store-expense/overview` | `mv_store_operating_expense_monthly` |
| `/store-expense/store-ranking` | `mv_store_operating_expense_monthly` |
| `/store-expense/store-contribution` | `mv_store_operating_expense_monthly` |
| `/store-expense/rent-risk` | `mv_store_operating_expense_monthly` |
| `/store-expense/delivery-commission` | `mv_store_operating_expense_monthly` |
| `/store-expense/efficiency` | `mv_store_operating_expense_monthly` |
| `/store-expense/fixed-variable` | `mv_store_operating_expense_monthly` |
| `/store-expense/break-even` | `mv_store_operating_expense_monthly` |
| `/store-expense/loss-diagnosis` | `mv_store_operating_expense_monthly` |
| `/store-expense/store-evaluation` | `mv_store_operating_expense_monthly` |
### 查询普通视图(实时,无需刷新)
| API | 数据源 |
|-----|--------|
| `/store-expense/expense-structure` | `v_operating_expense_account_monthly` |
| `/store-expense/data-quality` | `v_operating_expense_data_quality` |
| `/overview/daily` | `v_overview_daily` |
| `/time/weekday` | `v_weekday_summary` |
| `/time/hourly` | `v_hourly_summary` |
| `/channel` | `v_channel_daily` |
| `/data-quality` | `v_data_quality_check` |
### 查询 `dish_cost_analysis_summary`(原始表,按import_id
| API | 数据源 |
|-----|--------|
| `/cost-analysis/overview` | `dish_cost_analysis_summary` |
| `/cost-analysis/category-comparison` | `dish_cost_analysis_summary` |
| `/cost-analysis/margin-deviation` | `dish_cost_analysis_summary` |
| `/cost-analysis/variance-top` | `dish_cost_analysis_summary` |
---
## 四、已完成的修复
| 修复项 | 文件 | 说明 |
|--------|------|------|
| 利润瀑布排除无费用门店 | data.ts | 移除 `operating_expense IS NOT NULL` 过滤,改用 `COALESCE` |
| 费用总览排除无费用门店 | store-expense.ts | 移除过滤 |
| 费用视图重复行 | v_store_operating_expense_monthly | 增加 `expense_agg` CTE 按 `sales_store_code` 聚合 |
| 利润机会池负数 | data.ts:1234 | `GREATEST(..., 0)` 确保opportunity不为负 |
| 前端占比显示 | BossPage.tsx:136 | `totalOpportunity` 只累加正数 |
| 物化视图刷新 | 数据库 | 已刷新 `bill_fact``mv_store_risk_rating_monthly``mv_store_theoretical_actual_cost_monthly` |
| 费用物化表更新 | 数据库 | 已更新 `mv_store_operating_expense_monthly` |
---
## 五、待修复项
1. **`v_store_operating_expense_monthly` 视图硬编码 `_april`** — 需重构为通用月份
2. **`/overview/store-profit-ranking` 过滤** — 移除 `operating_expense IS NOT NULL`
3. **`/overview/profit-opportunity` 过滤** — 移除 `operating_expense IS NOT NULL`
4. **`/overview/profit-trend` 过滤** — 移除 `operating_expense IS NOT NULL`
5. **自动化物化视图刷新** — 建立数据导入后自动刷新机制
+99
View File
@@ -0,0 +1,99 @@
#!/bin/bash
# refresh_materialized_views.sh
# 数据导入后刷新所有物化视图并更新费用物化表
# 用法: bash scripts/refresh_materialized_views.sh [YYYY-MM-DD]
# 示例: bash scripts/refresh_materialized_views.sh 2026-04-01
set -e
MONTH_START="${1:-$(date -u +%Y-%m-01)}"
DB_HOST="${DB_HOST:-localhost}"
DB_PORT="${DB_PORT:-5432}"
DB_USER="${DB_USER:-freedak}"
DB_NAME="${DB_NAME:-bill_query}"
echo "=========================================="
echo " 刷新物化视图"
echo " 月份: ${MONTH_START}"
echo " 数据库: ${DB_NAME}@${DB_HOST}:${DB_PORT}"
echo "=========================================="
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" <<EOF
-- 第一层:账单事实
REFRESH MATERIALIZED VIEW analytics.bill_fact;
SELECT 'bill_fact refreshed' AS status;
-- 第二层:门店评估
REFRESH MATERIALIZED VIEW analytics.mv_store_risk_rating_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_theoretical_actual_cost_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_daily_revenue;
SELECT 'core MVs refreshed' AS status;
-- 第三层:菜品分析
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_abc_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_summary_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_store_summary_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_basket_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_pair_summary_monthly;
SELECT 'dish MVs refreshed' AS status;
-- 第四层:门店深度分析
REFRESH MATERIALIZED VIEW analytics.mv_store_action_priority_deep_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_area_efficiency_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_benchmark_composite_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_category_mix_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_deep_diagnosis_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_member_opportunity_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_overlap_risk_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_platform_economics_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_site_profile_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_site_replication_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_repeat_summary_monthly;
SELECT 'store deep MVs refreshed' AS status;
-- 第五层:区域与选址
REFRESH MATERIALIZED VIEW analytics.mv_district_site_benchmark_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_site_segment_benchmark_monthly;
SELECT 'region MVs refreshed' AS status;
-- 库存成本
REFRESH MATERIALIZED VIEW analytics.mv_inventory_cost_classified_monthly;
SELECT 'inventory MV refreshed' AS status;
-- 费用物化表(从视图写入)
DELETE FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '${MONTH_START}';
INSERT INTO analytics.mv_store_operating_expense_monthly
SELECT * FROM analytics.v_store_operating_expense_monthly WHERE report_month = '${MONTH_START}';
SELECT 'expense table updated' AS status;
-- 验证
SELECT '=== 数据一致性验证 ===' AS header;
SELECT 'bill_fact' AS source, count(DISTINCT store_code) AS stores, round(sum(received_total)::numeric,2) AS received
FROM analytics.bill_fact
WHERE closed_at >= '${MONTH_START}' AND closed_at < ('${MONTH_START}'::date + interval '1 month')
UNION ALL
SELECT 'risk_rating', count(*), round(sum(received)::numeric,2)
FROM analytics.mv_store_risk_rating_monthly WHERE month_start = '${MONTH_START}'
UNION ALL
SELECT 'theo_cost', count(*), round(sum(sales_received)::numeric,2)
FROM analytics.mv_store_theoretical_actual_cost_monthly WHERE month_start = '${MONTH_START}'
UNION ALL
SELECT 'op_expense', count(*), round(sum(received)::numeric,2)
FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '${MONTH_START}';
SELECT '=== 门店覆盖检查 ===' AS header;
SELECT r.store_code, r.store_name, r.received,
CASE WHEN e.sales_store_code IS NOT NULL THEN '有费用' ELSE '无费用' END AS expense_status
FROM analytics.mv_store_risk_rating_monthly r
LEFT JOIN analytics.mv_store_operating_expense_monthly e
ON r.store_code = e.sales_store_code AND e.report_month = '${MONTH_START}'
WHERE r.month_start = '${MONTH_START}' AND r.received > 0
AND e.sales_store_code IS NULL
ORDER BY r.received DESC;
EOF
echo "=========================================="
echo " 刷新完成!"
echo "=========================================="
+21 -19
View File
@@ -1093,15 +1093,15 @@ router.get('/overview/store-profit-ranking', async (req: AuthRequest, res) => {
r.store_code,
r.store_name,
round(r.received::numeric, 2) AS received,
round(e.actual_food_cost::numeric, 2) AS food_cost,
round(e.operating_expense::numeric, 2) AS expense,
round((r.received - e.actual_food_cost - e.operating_expense)::numeric, 2) AS store_contribution,
round((r.received - e.actual_food_cost - e.operating_expense) / nullif(r.received, 0) * 100, 2) AS contribution_margin_pct,
round(COALESCE(e.actual_food_cost, 0)::numeric, 2) AS food_cost,
round(COALESCE(e.operating_expense, 0)::numeric, 2) AS expense,
round((r.received - COALESCE(e.actual_food_cost, 0) - COALESCE(e.operating_expense, 0))::numeric, 2) AS store_contribution,
round((r.received - COALESCE(e.actual_food_cost, 0) - COALESCE(e.operating_expense, 0)) / nullif(r.received, 0) * 100, 2) AS contribution_margin_pct,
r.risk_level
FROM analytics.mv_store_risk_rating_monthly r
LEFT JOIN analytics.mv_store_operating_expense_monthly e
ON r.store_code = e.sales_store_code AND e.report_month = $1::date
WHERE r.month_start = $1 AND r.received IS NOT NULL AND r.received > 0 AND e.operating_expense IS NOT NULL
WHERE r.month_start = $1 AND r.received IS NOT NULL AND r.received > 0
ORDER BY store_contribution DESC
`, [month])
sendSuccess(res, result.rows)
@@ -1123,13 +1123,15 @@ router.get('/overview/profit-opportunity', async (req: AuthRequest, res) => {
),
standard_stores AS (
SELECT r.store_code, r.store_name, r.received,
e.actual_food_cost, e.operating_expense,
e.wage_expense, e.utility_expense,
COALESCE(e.actual_food_cost, 0) AS actual_food_cost,
COALESCE(e.operating_expense, 0) AS operating_expense,
COALESCE(e.wage_expense, 0) AS wage_expense,
COALESCE(e.utility_expense, 0) AS utility_expense,
r.theoretical_margin_pct, r.discount_rate_pct
FROM risk r
LEFT JOIN analytics.mv_store_operating_expense_monthly e
ON r.store_code = e.sales_store_code AND e.report_month = $1::date
WHERE r.received IS NOT NULL AND r.received > 0 AND e.operating_expense IS NOT NULL
WHERE r.received IS NOT NULL AND r.received > 0
),
cost_diff AS (
SELECT
@@ -1246,7 +1248,7 @@ router.get('/overview/profit-opportunity', async (req: AuthRequest, res) => {
'category', '标准店人工优化',
'baseline', (SELECT round(total_wage / total_received * 100, 2) FROM labor),
'target_pct', 100,
'opportunity', round((SELECT total_wage * (1 - 24.0 / nullif(total_wage / total_received * 100, 0)) FROM labor), 2),
'opportunity', GREATEST(round((SELECT total_wage * (1 - 24.0 / nullif(total_wage / total_received * 100, 0)) FROM labor), 2), 0),
'confidence', '中',
'owner', '运营/人力',
'evidence', '工时、工资、餐段销售与服务质量',
@@ -1261,7 +1263,7 @@ router.get('/overview/profit-opportunity', async (req: AuthRequest, res) => {
'category', '标准店能源优化',
'baseline', (SELECT round(total_utility / total_received * 100, 2) FROM energy),
'target_pct', 100,
'opportunity', round((SELECT total_utility * (1 - 5.0 / nullif(total_utility / total_received * 100, 0)) FROM energy), 2),
'opportunity', GREATEST(round((SELECT total_utility * (1 - 5.0 / nullif(total_utility / total_received * 100, 0)) FROM energy), 2), 0),
'confidence', '中',
'owner', '工程/门店',
'evidence', '账单/抄表、面积、营业时长',
@@ -1423,21 +1425,21 @@ router.get('/overview/profit-trend', async (req: AuthRequest, res) => {
),
monthly AS (
SELECT e.report_month,
round(sum(e.consumption)::numeric, 2) AS consumption,
round(sum(COALESCE(e.consumption, 0))::numeric, 2) AS consumption,
round(max(bm.discount)::numeric, 2) AS discount,
round(sum(r.received)::numeric, 2) AS received,
round(sum(e.actual_food_cost)::numeric, 2) AS food_cost,
round(sum(e.wage_expense)::numeric, 2) AS wage,
round(sum(e.rent_expense)::numeric, 2) AS rent,
round(sum(e.utility_expense)::numeric, 2) AS utility,
round(sum(e.operating_expense)::numeric, 2) AS total_expense,
round(sum(r.received) - sum(e.actual_food_cost) - sum(e.operating_expense), 2) AS store_contribution
round(sum(COALESCE(e.actual_food_cost, 0))::numeric, 2) AS food_cost,
round(sum(COALESCE(e.wage_expense, 0))::numeric, 2) AS wage,
round(sum(COALESCE(e.rent_expense, 0))::numeric, 2) AS rent,
round(sum(COALESCE(e.utility_expense, 0))::numeric, 2) AS utility,
round(sum(COALESCE(e.operating_expense, 0))::numeric, 2) AS total_expense,
round(sum(r.received) - sum(COALESCE(e.actual_food_cost, 0)) - sum(COALESCE(e.operating_expense, 0)), 2) AS store_contribution
FROM analytics.v_store_scorecard r
JOIN analytics.mv_store_operating_expense_monthly e
LEFT JOIN analytics.mv_store_operating_expense_monthly e
ON r.store_code = e.sales_store_code
LEFT JOIN bill_monthly bm ON bm.report_month = e.report_month
WHERE e.report_month >= $1::date AND e.report_month <= $2::date
AND e.operating_expense IS NOT NULL AND r.received > 0
AND r.received > 0
GROUP BY e.report_month
ORDER BY e.report_month
)
+233
View File
@@ -0,0 +1,233 @@
# 原始数据导入处理
## 一、数据源概览
| 数据域 | 原始表 | 导入方式 | 文件格式 | 频率 |
|--------|--------|----------|----------|------|
| 账单数据 | `bill_records` | Python脚本导入 | Excel (.xlsx) | 月度 |
| 经营费用 | `operating_expense_records` | Python脚本导入 | Excel | 月度 |
| 库存成本 | `inventory_cost_records` | Python脚本导入 | Excel | 月度 |
| 菜品成本分析 | `dish_cost_analysis_summary` | Python脚本导入 | Excel | 月度 |
| 考勤数据 | `attendance_records` | Python脚本导入 | Excel | 月度 |
| 薪资数据 | `salary_detail_records` | Python脚本导入 | Excel | 月度 |
| 门店位置 | `store_location_master` | Python脚本导入 | Excel | 按需 |
| 中央厨房 | `central_kitchen_*` (6表) | Python脚本导入 | Excel | 月度 |
| 配送数据 | `distribution_detail_records` | Python脚本导入 | Excel | 月度 |
| 菜品销售 | `dish_sales_details` | Python脚本导入 | Excel | 月度 |
---
## 二、账单数据导入(bill_records
### 2.1 数据源
- **来源文件**: 正餐事业部_店内订单明细-已结账_*.xlsx
- **来源系统**: 餐饮POS系统导出
- **文件路径**: `数据/4月/``数据/5月/`
### 2.2 导入脚本
| 脚本 | 说明 |
|------|------|
| `/tmp/import_missing_stores.py` | 导入缺失门店账单(MD00002/MD00003/MD00008/1098/1110 |
| `/tmp/import_remaining_stores.py` | 导入剩余门店账单(哈马尔罕总部基地店0056/大钟寺店0022/茂林居店0021/西安含光店0052/西部马华火锅北三环店0012) |
### 2.3 字段映射
`bill_records` 表使用 c001~c199 编码,关键字段:
| 列号 | 字段含义 | 用途 |
|------|----------|------|
| c002 | 门店编码 | 门店标识 |
| c003 | 门店名称 | 门店名称(用于JOIN) |
| c009 | 消费金额 | 营业收入 |
| c068 | 优惠金额 | 优惠分析 |
| c114 | 理论成本额 | 理论毛利率 |
| c175 | 下单时间 | 时段分析 |
| c176 | 结账时间 | 日度汇总 |
| c178 | 就餐人数 | 客流分析 |
| c181 | 会员卡号 | 会员分析 |
| c185 | 会员标识 | 会员识别 |
### 2.4 导入逻辑
1. 读取 Excel 文件,解析支付方式和金额
2. 按门店名称匹配 `store_name_mapping`
3. **删除旧数据**: 按 `c003`(门店名称)和日期范围删除同月旧记录
4. **插入新数据**: 批量 INSERT,自动生成 bill_id
5. 导入后需刷新 `bill_fact` 物化视图
### 2.5 支付方式解析
脚本中定义了支付方式映射,将不同支付渠道(微信/支付宝/现金/会员储值/美团/饿了么等)解析为对应字段。
### 2.6 门店名称映射
| 原始名称 | 门店编码 | 说明 |
|----------|----------|------|
| 哈马尔罕(总部基地店) | 0056 | 新导入 |
| 哈马尔罕(大钟寺店) | 0022 | 新导入 |
| 哈马尔罕(茂林居店) | 0021 | 新导入 |
| 哈马尔罕(西安含光店) | 0052 | 新导入 |
| 西部马华火锅(北三环店) | 0012 | 新导入 |
| 关东店 | 1098 | 编码修正(原错误映射为0033) |
---
## 三、经营费用导入(operating_expense_records
### 3.1 数据源
- **来源文件**: 各门店费用明细Excel
- **费用科目**: 月薪员工工资、餐厅房租、外卖佣金、电费、燃气费、水费、物业费、宿舍费用、刷卡手续费、清洗费、垃圾费、维修费、菜品提成、自送快递费、烟道清洗、设备租赁费、暖气费
### 3.2 导入流程
1. 读取 Excel,按费用科目和门店单位导入
2. 写入 `operating_expense_records` 原始表
3. 通过 `operating_expense_store_mapping` 映射到销售门店编码
4. `include_in_operating_analysis` 标志控制是否纳入经营分析
### 3.3 门店映射规则
- 一个费用单位可映射到一个销售门店编码(`sales_store_code`
- 多个费用单位可映射到同一门店(如"哈马尔罕大钟寺店"→"大钟寺店"0022
- 未映射的费用单位(支持/供应链/特殊业务)不纳入门店评估
- `include_in_operating_analysis = true` 才参与费用分析
### 3.4 导入后处理
- 视图 `v_operating_expense_unit_monthly` 自动反映新数据
- 视图 `v_store_operating_expense_monthly``sales_store_code` 聚合
- 物化表 `mv_store_operating_expense_monthly` 需手动刷新(INSERT/UPDATE
---
## 四、库存成本导入(inventory_cost_records
### 4.1 数据源
- **来源文件**: 库存盘点Excel
- **内容**: 各门店库存消耗金额、原料分类
### 4.2 导入流程
1. 读取 Excel,按门店和原料分类导入
2. 写入 `inventory_cost_records` 原始表
3. 通过 `inventory_store_mapping` 映射到销售门店编码
4. `include_in_operating_cost` 标志控制是否纳入成本分析
### 4.3 导入后处理
- 刷新 `mv_inventory_cost_classified_monthly` 物化视图
- 刷新 `mv_store_theoretical_actual_cost_monthly` 物化视图
---
## 五、菜品成本分析导入(dish_cost_analysis_summary
### 5.1 数据源
- **来源文件**: 菜品成本分析Excel
- **内容**: 每个菜品的理论成本、实际成本、毛利率
### 5.2 导入流程
1. 读取 Excel,按菜品导入
2. 写入 `dish_cost_analysis_summary``dish_cost_analysis_material_detail`
3. 记录导入日志到 `dish_cost_analysis_import_log`
4.`report_month``import_id` 区分批次
---
## 六、考勤薪资导入
### 6.1 考勤数据
- **原始表**: `attendance_records`38列)
- **导入日志**: `attendance_import_log`
- **内容**: 员工打卡记录(上班/下班/迟到/早退)
### 6.2 薪资数据
- **原始表**: `salary_detail_records`102列)
- **导入日志**: `salary_import_log`
- **内容**: 员工薪资明细(基本工资/加班/社保/公积金等)
---
## 七、门店位置导入(store_location_master
### 7.1 数据源
- **来源文件**: 门店位置信息Excel
- **内容**: 门店地址、经纬度(GCJ02)、面积、租约到期日、营业时间
### 7.2 导入流程
1. 读取 Excel,写入 `store_location_source_rows` 原始行
2. 匹配到 `sales_store_location_mapping` 建立门店编码关联
3. 更新 `store_location_master` 主数据
### 7.3 关键字段
| 字段 | 说明 |
|------|------|
| `area_sqm` | 营业面积(平米),用于坪效计算 |
| `lease_expiry_date` | 租约到期日,用于续租风险 |
| `latitude_gcj02` / `longitude_gcj02` | GCJ02坐标,用于地图展示 |
| `business_address` | 营业地址 |
---
## 八、数据导入后的标准操作流程
```
1. 导入原始数据到 public.* 表
2. 刷新 bill_fact 物化视图
REFRESH MATERIALIZED VIEW analytics.bill_fact;
3. 刷新依赖 bill_fact 的物化视图
REFRESH MATERIALIZED VIEW analytics.mv_daily_revenue;
REFRESH MATERIALIZED VIEW analytics.mv_store_risk_rating_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_theoretical_actual_cost_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_abc_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_summary_monthly;
... (所有依赖 bill_fact 的物化视图)
4. 更新 mv_store_operating_expense_monthly 物化表
(从 v_store_operating_expense_monthly 视图 INSERT/UPDATE
5. 刷新库存成本物化视图
REFRESH MATERIALIZED VIEW analytics.mv_inventory_cost_classified_monthly;
6. 验证数据一致性
- 对比 bill_fact 和 mv_store_risk_rating_monthly 门店数
- 对比 bill_fact 和 mv_store_theoretical_actual_cost_monthly 实收合计
- 检查是否有遗漏门店
```
---
## 九、已知问题与修复记录
### 9.1 门店编码映射错误
- **问题**: 关东店编码错误映射为0033,实际应为1098
- **修复**: 更新 `store_name_mapping`
### 9.2 旧数据误删
- **问题**: 导入新数据时误删了旧账单记录(bill_records),导致历史数据丢失
- **修复**: 从数据库备份 `bill_query_full_20260802_150344.dump` 恢复,使用 `pg_restore` 恢复到临时库后提取缺失数据
### 9.3 费用单位重复映射
- **问题**: "哈马尔罕大钟寺店"的费用记录未映射到"大钟寺店"(0022),导致费用未纳入门店评估
- **修复**: 更新 `operating_expense_store_mapping`,将 `sales_store_code` 设为 0022`include_in_operating_analysis = true`
### 9.4 视图聚合问题
- **问题**: `v_store_operating_expense_monthly` 视图未按 `sales_store_code` 聚合,导致多个费用单位映射到同一门店时出现重复行
- **修复**: 重建视图,增加 `expense_agg` CTE 按 `sales_store_code` GROUP BY 聚合
+401
View File
@@ -0,0 +1,401 @@
# 系统本体结构描述
## 一、数据库架构
- **数据库引擎**: PostgreSQL 15 (Homebrew)
- **数据库名**: bill_query
- **Schema 分布**: `public`(原始数据)、`analytics`(分析层)
---
## 二、原始数据表(public schema
### 2.1 账单数据
| 表名 | 列数 | 说明 |
|------|------|------|
| `bill_records` | 199 | 账单明细原始表,每行一笔账单,字段以 c001~c199 编码 |
| `bill_columns` | 4 | 账单字段元数据(列号→含义映射) |
**关键字段映射**:
- `c002` = 门店名称,`c003` = 门店名称(冗余),`c009` = 消费金额,`c068` = 优惠金额
- `c175` = 下单时间,`c176` = 结账时间,`c178` = 就餐人数
- `c181` = 会员卡号,`c185` = 会员标识
- `c114` = 理论成本额,`c009` = 消费额(用于理论毛利率计算)
### 2.2 费用数据
| 表名 | 列数 | 说明 |
|------|------|------|
| `operating_expense_records` | 17 | 经营费用原始记录(工资/房租/水电/佣金等) |
| `operating_expense_store_mapping` | 10 | 费用单位→销售门店编码映射,含 `include_in_operating_analysis` 标志 |
| `operating_expense_import_log` | 8 | 费用导入日志 |
### 2.3 库存成本数据
| 表名 | 列数 | 说明 |
|------|------|------|
| `inventory_cost_records` | 42 | 库存盘点成本原始记录 |
| `inventory_store_mapping` | 10 | 库存单位→销售门店编码映射,含 `include_in_operating_cost` 标志 |
| `inventory_cost_import_log` | 7 | 库存成本导入日志 |
### 2.4 菜品成本分析
| 表名 | 列数 | 说明 |
|------|------|------|
| `dish_cost_analysis_summary` | 18 | 菜品成本分析汇总(理论 vs 实际毛利率) |
| `dish_cost_analysis_material_detail` | 19 | 菜品原料明细 |
| `dish_cost_analysis_import_log` | 17 | 菜品成本分析导入日志 |
### 2.5 考勤薪资数据
| 表名 | 列数 | 说明 |
|------|------|------|
| `attendance_records` | 38 | 考勤打卡记录 |
| `attendance_import_log` | 7 | 考勤导入日志 |
| `salary_detail_records` | 102 | 薪资明细记录 |
| `salary_import_log` | 7 | 薪资导入日志 |
### 2.6 门店位置数据
| 表名 | 列数 | 说明 |
|------|------|------|
| `store_location_master` | 34 | 门店位置主数据(地址/经纬度/面积/租约等) |
| `store_location_source_rows` | 13 | 门店位置原始行 |
| `store_location_import_log` | 10 | 位置导入日志 |
| `sales_store_location_mapping` | 8 | 销售门店→位置映射 |
| `store_name_mapping` | 2 | 门店名称映射表 |
### 2.7 中央厨房数据
| 表名 | 列数 | 说明 |
|------|------|------|
| `central_kitchen_finished_receipt` | 34 | 成品入库记录 |
| `central_kitchen_manufacturing_cost_pool` | 14 | 制造费用池 |
| `central_kitchen_material_daily` | 36 | 原料日耗明细 |
| `central_kitchen_processing_cost` | 29 | 加工成本 |
| `central_kitchen_recipe_consumption` | 39 | 配方消耗 |
| `central_kitchen_import_log` | 13 | 中央厨房导入日志 |
### 2.8 配送数据
| 表名 | 列数 | 说明 |
|------|------|------|
| `distribution_detail_records` | 85 | 配送明细记录 |
| `distribution_import_log` | 17 | 配送导入日志 |
### 2.9 菜品调整
| 表名 | 列数 | 说明 |
|------|------|------|
| `dish_adjustment_log` | 20 | 菜品调整日志 |
| `dish_adjustment_result` | 15 | 菜品调整结果 |
| `dish_diagnosis_snapshot` | 19 | 菜品诊断快照 |
| `dish_sales_details` | 24 | 菜品销售明细 |
| `dish_sales_import_log` | 6 | 菜品销售导入日志 |
---
## 三、分析层维度表(analytics schema
| 表名 | 列数 | 说明 |
|------|------|------|
| `dim_store` | 14 | 门店维度(编码/名称/类型/区域) |
| `dim_calendar` | 14 | 日历维度 |
| `dim_channel` | 10 | 渠道维度 |
| `dim_daily_target` | 12 | 日度目标 |
| `dim_employee` | 9 | 员工维度 |
| `dim_material` | 12 | 原料维度 |
| `dim_member` | 13 | 会员维度 |
| `dim_product_lifecycle` | 16 | 产品生命周期 |
| `dim_promotion` | 15 | 促销维度 |
| `dim_sku` | 21 | SKU维度 |
| `dim_store_target` | 11 | 门店目标 |
| `dim_supplier` | 9 | 供应商维度 |
---
## 四、分析层事实表(analytics schema
| 表名 | 列数 | 说明 |
|------|------|------|
| `fact_bill` | 52 | 账单事实表(标准化) |
| `fact_bill_item` | 25 | 账单明细事实 |
| `fact_payment` | 9 | 支付事实 |
| `fact_customer_contact` | 13 | 客户联系事实 |
| `fact_employee_shift` | 13 | 员工排班事实 |
| `fact_inspection` | 17 | 巡检事实 |
| `fact_inventory_snapshot` | 19 | 库存快照事实 |
| `fact_iot_reading` | 11 | IoT读数事实 |
| `fact_platform_order` | 16 | 平台订单事实 |
| `fact_promotion_usage` | 12 | 促销使用事实 |
| `fact_purchase_receipt` | 16 | 采购入库事实 |
| `fact_recipe_bom` | 14 | BOM配方事实 |
| `fact_review` | 14 | 评价事实 |
| `fact_scan_confirm` | 13 | 扫描确认事实 |
| `fact_store_task` | 22 | 门店任务事实 |
| `fact_transfer` | 12 | 调拨事实 |
| `fact_waste` | 11 | 报损事实 |
---
## 五、枚举表(analytics schema
| 表名 | 说明 |
|------|------|
| `enum_business_type` | 业务类型 |
| `enum_channel_group` | 渠道分组 |
| `enum_management_quadrant` | 管理象限 |
| `enum_member_status` | 会员状态 |
| `enum_metric_direction` | 指标方向 |
| `enum_priority` | 优先级 |
| `enum_risk_level` | 风险等级 |
| `enum_sku_status` | SKU状态 |
| `enum_sku_type` | SKU类型 |
| `enum_task_status` | 任务状态 |
| `enum_verification_result` | 验证结果 |
| `enum_waste_reason` | 报损原因 |
---
## 六、物化视图列表(analytics schema
### 6.1 核心账单物化视图
| 物化视图 | 说明 | 依赖原始表 |
|----------|------|------------|
| `bill_fact` | 账单事实物化视图,从 bill_records 标准化 | `bill_records` |
| `mv_daily_revenue` | 日度营收汇总 | `bill_records` |
### 6.2 门店评估物化视图
| 物化视图 | 说明 | 依赖 |
|----------|------|------|
| `mv_store_risk_rating_monthly` | 门店风险评级(月度) | `bill_fact` |
| `mv_store_operating_expense_monthly` | 门店经营费用(月度,物化表) | `operating_expense_records` + mapping |
| `mv_store_theoretical_actual_cost_monthly` | 门店理论 vs 实际成本(月度) | `bill_fact` + `inventory_cost_records` |
| `mv_store_action_priority_deep_monthly` | 门店行动优先级深度分析 | `bill_fact` |
| `mv_store_deep_diagnosis_monthly` | 门店深度诊断 | `bill_fact` |
| `mv_store_scorecard` | 门店记分卡 | `bill_fact` |
### 6.3 门店基准与效率物化视图
| 物化视图 | 说明 | 依赖 |
|----------|------|------|
| `mv_store_benchmark_composite_monthly` | 门店综合基准 | `bill_fact` |
| `mv_store_area_efficiency_monthly` | 门店坪效分析 | `bill_fact` |
| `mv_store_platform_economics_monthly` | 门店平台经济分析 | `bill_fact` |
| `mv_store_category_mix_monthly` | 门店品类结构 | `bill_fact` |
| `mv_store_member_opportunity_monthly` | 门店会员机会 | `bill_fact` |
| `mv_store_overlap_risk_monthly` | 门店重叠风险 | `bill_fact` |
| `mv_store_site_profile_monthly` | 门店选址画像 | `bill_fact` |
| `mv_store_site_replication_monthly` | 门店选址复制 | `bill_fact` |
| `mv_store_repeat_summary_monthly` | 门店复购汇总 | `bill_fact` |
### 6.4 菜品分析物化视图
| 物化视图 | 说明 | 依赖 |
|----------|------|------|
| `mv_dish_sku_abc_monthly` | 菜品SKU ABC分类 | `bill_fact` |
| `mv_dish_sku_summary_monthly` | 菜品SKU汇总 | `bill_fact` |
| `mv_dish_store_summary_monthly` | 门店菜品汇总 | `bill_fact` |
| `mv_dish_basket_monthly` | 菜品购物篮分析 | `bill_fact` |
| `mv_dish_pair_summary_monthly` | 菜品搭配分析 | `bill_fact` |
### 6.5 选址与区域物化视图
| 物化视图 | 说明 | 依赖 |
|----------|------|------|
| `mv_district_site_benchmark_monthly` | 区域选址基准 | `bill_fact` |
| `mv_site_segment_benchmark_monthly` | 分段选址基准 | `bill_fact` |
### 6.6 库存成本物化视图
| 物化视图 | 说明 | 依赖 |
|----------|------|------|
| `mv_inventory_cost_classified_monthly` | 库存成本分类汇总 | `inventory_cost_records` + mapping |
### 6.7 固定月份物化视图(2026年4月专用)
| 物化视图 | 说明 |
|----------|------|
| `mv_store_theoretical_actual_cost_april` | 4月理论 vs 实际成本 |
| `mv_store_site_profile_april` | 4月门店选址画像 |
| `mv_store_action_priority_deep_april` | 4月行动优先级 |
| `dish_sales_april` | 4月菜品销售 |
| `dish_sku_summary_april` | 4月SKU汇总 |
| `dish_store_summary_april` | 4月门店菜品汇总 |
| `dish_basket_april` | 4月购物篮 |
| `dish_category_summary_april` | 4月品类汇总 |
| `dish_member_sku_april` | 4月会员SKU |
| `dish_store_sku_april` | 4月门店SKU |
---
## 七、普通视图列表(analytics schema
### 7.1 费用相关视图
| 视图 | 说明 | 依赖 |
|------|------|------|
| `v_operating_expense_unit_monthly` | 费用单位月度汇总 | `operating_expense_records` + mapping |
| `v_store_operating_expense_monthly` | 门店费用月度汇总(按 sales_store_code 聚合) | `v_operating_expense_unit_monthly` |
| `v_operating_expense_account_monthly` | 费用科目月度汇总 | `operating_expense_records` |
| `v_operating_expense_data_quality` | 费用数据质量 | `operating_expense_records` + mapping |
### 7.2 门店分析视图
| 视图 | 说明 |
|------|------|
| `v_store_location_operating` | 门店位置经营信息 |
| `v_store_location_data_quality` | 门店位置数据质量 |
| `v_store_daily` | 门店日度数据 |
| `v_store_daily_card` | 门店日度卡片 |
| `v_store_meal_opportunity` | 门店餐段机会 |
| `v_store_member_monthly_activity` | 门店会员月度活动 |
| `v_store_monthly_followup` | 门店月度跟进 |
| `v_store_nearest_neighbor_april` | 门店最近邻 |
| `v_store_spatial_pairs_april` | 门店空间配对 |
### 7.3 营收分析视图
| 视图 | 说明 |
|------|------|
| `v_overview_daily` | 日度总览 |
| `v_hourly_summary` | 小时汇总 |
| `v_weekday_summary` | 星期汇总 |
| `v_meal_period_daily` | 餐段日度 |
| `v_channel_daily` | 渠道日度 |
| `v_region_summary` | 区域汇总 |
### 7.4 风险与异常视图
| 视图 | 说明 |
|------|------|
| `v_anomaly_bills` | 异常账单 |
| `v_anomaly_summary` | 异常汇总 |
| `v_cashier_risk` | 收银风险 |
| `v_zero_received_store_summary` | 零实收门店汇总 |
| `v_zero_received_detail` | 零实收明细 |
### 7.5 成本相关视图
| 视图 | 说明 |
|------|------|
| `v_inventory_cost_operating` | 库存成本经营 |
| `v_inventory_cost_unit_summary` | 库存成本单位汇总 |
| `v_inventory_finance_category_april` | 库存财务分类 |
| `v_inventory_abnormal_items_april` | 库存异常项 |
| `v_cost_linkage_*` | 成本联动系列视图 |
### 7.6 中央厨房视图
| 视图 | 说明 |
|------|------|
| `v_central_kitchen_*` | 中央厨房系列视图(6个) |
| `v_distribution_*` | 配送系列视图(4个) |
### 7.7 任务与指标视图
| 视图 | 说明 |
|------|------|
| `v_task_monthly_review` | 任务月度复盘 |
| `v_task_weekly_check` | 任务周度检查 |
| `v_loop_health` | 闭环健康度 |
| `v_marketing_plan_summary` | 营销计划汇总 |
| `v_member_comparison` | 会员对比 |
| `v_member_level_mapping` | 会员等级映射 |
| `v_member_monthly_activity` | 会员月度活动 |
| `v_member_repeat_summary_monthly` | 会员复购汇总 |
| `v_position_mapping` | 岗位映射 |
| `v_data_quality_check` | 数据质量检查 |
---
## 八、V3.0 业务管理表(analytics schema
| 表名 | 说明 |
|------|------|
| `v3_annual_target` | 年度目标 |
| `v3_regional_target` | 区域目标 |
| `v3_store_monthly_target` | 门店月度目标 |
| `v3_daily_target` | 日度目标 |
| `v3_store_grade` | 门店分级 |
| `v3_store_roi` | 门店ROI |
| `v3_alert_rule` / `v3_alert_log` | 预警规则/日志 |
| `v3_approval_*` | 审批流(3表) |
| `v3_budget` / `v3_budget_lock` | 预算/预算锁 |
| `v3_coupon` | 优惠券 |
| `v3_marketing_campaign` | 营销活动 |
| `v3_forecast` | 预测 |
| `v3_monthly_report` | 月报 |
| `v3_notification` | 通知 |
| `v3_purchase_order` / `v3_purchase_order_item` | 采购订单 |
| `v3_supplier_master` | 供应商主数据 |
| `v3_inventory_snapshot` | 库存快照 |
| `v3_iot_device` / `v3_iot_temperature` | IoT设备/温度 |
| `v3_loop_verification` | 闭环验证 |
| `v3_learning_record` / `v3_training_course` | 培训学习 |
| `v3_product_lifecycle` | 产品生命周期 |
| `v3_scheduled_task` / `v3_personal_task` | 计划任务/个人任务 |
| `v3_store_grade` | 门店分级 |
| `v3_target_variance` | 目标偏差 |
| `v3_ai_target_adjustment` | AI目标调整 |
| `v3_audit_log` | 审计日志 |
| `v3_brand_asset` | 品牌资产 |
| `v3_customer_review` | 客户评价 |
| `v3_data_import_log` / `v3_data_quality_rule` | 数据导入/质量规则 |
---
## 九、数据流架构
```
原始数据导入 分析层构建 API serving
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ bill_records │──REFRESH──▶│ bill_fact │◀──QUERY───│ Express API │
│ (199 cols) │ │ (52 cols) │ │ (18 routes) │
└─────────────┘ └──────────────┘ └──────────────┘
│ │
│ REFRESH ▼
│ ┌──────────────────────┐
│ │ mv_store_risk_rating │
│ │ _monthly │
│ └──────────────────────┘
│ │
│ REFRESH ▼
│ ┌────────────────────────────────┐
│ │ mv_store_theoretical_actual_ │
│ │ cost_monthly │
│ └────────────────────────────────┘
┌──────────────────────┐ ┌──────────────────────────┐
│ operating_expense_ │────▶│ v_operating_expense_ │
│ records + mapping │ │ unit_monthly (view) │
└──────────────────────┘ └──────────────────────────┘
VIEW ▼
┌──────────────────────────┐
│ v_store_operating_ │
│ expense_monthly (view) │
│ (按 sales_store_code 聚合)│
└──────────────────────────┘
INSERT ▼
┌──────────────────────────┐
│ mv_store_operating_ │
│ expense_monthly (table) │
└──────────────────────────┘
┌──────────────────────┐ ┌──────────────────────────┐
│ inventory_cost_ │────▶│ mv_inventory_cost_ │
│ records + mapping │ │ classified_monthly (MV) │
└──────────────────────┘ └──────────────────────────┘
```
**关键设计**:
- `bill_fact` 是所有账单分析的基础物化视图,从 `bill_records` 标准化而来
- `mv_store_operating_expense_monthly` 是物化**表**(非视图),需手动 INSERT/UPDATE
- `v_store_operating_expense_monthly` 是普通视图,实时查询费用数据
- 部分API直接查 `bill_records`(如门店日度、排班分析),部分查 `bill_fact`
- 固定月份物化视图(`*_april`)硬编码2026年4月,不随月份参数变化
+302
View File
@@ -0,0 +1,302 @@
# 物化视图更新手册
## 一、物化视图依赖链
数据更新后必须按依赖顺序刷新物化视图,否则下游数据不一致。
```
bill_records (原始表)
▼ REFRESH
bill_fact (物化视图) ★ 第一层,最关键
├─▶ mv_daily_revenue
├─▶ mv_store_risk_rating_monthly ★ 门店评估基础
├─▶ mv_dish_sku_abc_monthly
├─▶ mv_dish_sku_summary_monthly
├─▶ mv_dish_store_summary_monthly
├─▶ mv_dish_basket_monthly
├─▶ mv_dish_pair_summary_monthly
├─▶ mv_store_action_priority_deep_monthly
├─▶ mv_store_area_efficiency_monthly
├─▶ mv_store_benchmark_composite_monthly
├─▶ mv_store_category_mix_monthly
├─▶ mv_store_deep_diagnosis_monthly
├─▶ mv_store_member_opportunity_monthly
├─▶ mv_store_overlap_risk_monthly
├─▶ mv_store_platform_economics_monthly
├─▶ mv_store_site_profile_monthly
├─▶ mv_store_site_replication_monthly
├─▶ mv_store_repeat_summary_monthly
├─▶ mv_district_site_benchmark_monthly
├─▶ mv_site_segment_benchmark_monthly
└─▶ mv_store_theoretical_actual_cost_monthly ★ 门店成本
inventory_cost_records (原始表)
▼ REFRESH
mv_inventory_cost_classified_monthly
operating_expense_records (原始表)
▼ VIEW (无需刷新)
v_operating_expense_unit_monthly
▼ VIEW (无需刷新)
v_store_operating_expense_monthly (按 sales_store_code 聚合)
▼ INSERT/UPDATE (需手动操作)
mv_store_operating_expense_monthly (物化表)
```
---
## 二、刷新命令清单
### 2.1 账单数据更新后(必刷)
```sql
-- ★ 第一优先级:bill_fact 是所有分析的基础
REFRESH MATERIALIZED VIEW analytics.bill_fact;
-- ★ 第二优先级:门店评估相关
REFRESH MATERIALIZED VIEW analytics.mv_store_risk_rating_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_theoretical_actual_cost_monthly;
-- 第三优先级:菜品分析
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_abc_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_summary_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_store_summary_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_basket_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_pair_summary_monthly;
-- 第四优先级:门店深度分析
REFRESH MATERIALIZED VIEW analytics.mv_store_action_priority_deep_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_area_efficiency_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_benchmark_composite_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_category_mix_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_deep_diagnosis_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_member_opportunity_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_overlap_risk_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_platform_economics_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_site_profile_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_site_replication_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_repeat_summary_monthly;
-- 第五优先级:日度与区域
REFRESH MATERIALIZED VIEW analytics.mv_daily_revenue;
REFRESH MATERIALIZED VIEW analytics.mv_district_site_benchmark_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_site_segment_benchmark_monthly;
```
### 2.2 库存成本数据更新后
```sql
REFRESH MATERIALIZED VIEW analytics.mv_inventory_cost_classified_monthly;
-- 理论vs实际成本也依赖库存数据
REFRESH MATERIALIZED VIEW analytics.mv_store_theoretical_actual_cost_monthly;
```
### 2.3 费用数据更新后
```sql
-- v_operating_expense_unit_monthly 和 v_store_operating_expense_monthly 是普通视图,无需刷新
-- 但 mv_store_operating_expense_monthly 是物化表,需要手动更新:
-- 方法1:删除+插入(推荐,简单可靠)
DELETE FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '2026-04-01';
INSERT INTO analytics.mv_store_operating_expense_monthly
SELECT * FROM analytics.v_store_operating_expense_monthly WHERE report_month = '2026-04-01';
-- 方法2:全量重建
TRUNCATE analytics.mv_store_operating_expense_monthly;
INSERT INTO analytics.mv_store_operating_expense_monthly
SELECT * FROM analytics.v_store_operating_expense_monthly;
```
---
## 三、一键刷新脚本
```bash
#!/bin/bash
# refresh_all_mv.sh - 刷新所有物化视图
# 用法: bash refresh_all_mv.sh [month] (默认 2026-04)
MONTH=${1:-2026-04}
MONTH_START="${MONTH}-01"
psql -h localhost -p 5432 -U freedak -d bill_query <<EOF
-- 第一层
REFRESH MATERIALIZED VIEW analytics.bill_fact;
-- 第二层
REFRESH MATERIALIZED VIEW analytics.mv_store_risk_rating_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_theoretical_actual_cost_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_daily_revenue;
-- 第三层
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_abc_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_summary_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_store_summary_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_basket_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_dish_pair_summary_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_action_priority_deep_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_area_efficiency_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_benchmark_composite_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_category_mix_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_deep_diagnosis_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_member_opportunity_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_overlap_risk_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_platform_economics_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_site_profile_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_site_replication_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_store_repeat_summary_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_district_site_benchmark_monthly;
REFRESH MATERIALIZED VIEW analytics.mv_site_segment_benchmark_monthly;
-- 库存成本
REFRESH MATERIALIZED VIEW analytics.mv_inventory_cost_classified_monthly;
-- 费用物化表
DELETE FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '${MONTH_START}';
INSERT INTO analytics.mv_store_operating_expense_monthly
SELECT * FROM analytics.v_store_operating_expense_monthly WHERE report_month = '${MONTH_START}';
-- 验证
SELECT 'bill_fact' AS source, count(DISTINCT store_code) AS stores, round(sum(received_total)::numeric,2) AS received
FROM analytics.bill_fact WHERE closed_at >= '${MONTH_START}' AND closed_at < ('${MONTH_START}'::date + interval '1 month')
UNION ALL
SELECT 'risk_rating', count(*), round(sum(received)::numeric,2)
FROM analytics.mv_store_risk_rating_monthly WHERE month_start = '${MONTH_START}'
UNION ALL
SELECT 'theo_cost', count(*), round(sum(sales_received)::numeric,2)
FROM analytics.mv_store_theoretical_actual_cost_monthly WHERE month_start = '${MONTH_START}'
UNION ALL
SELECT 'op_expense', count(*), round(sum(received)::numeric,2)
FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '${MONTH_START}';
EOF
```
---
## 四、物化视图 vs 普通视图 vs 物化表
| 类型 | 刷新方式 | 示例 | 说明 |
|------|----------|------|------|
| 物化视图 (Materialized View) | `REFRESH MATERIALIZED VIEW` | `bill_fact`, `mv_store_risk_rating_monthly` | 数据快照,需手动刷新 |
| 普通视图 (View) | 实时查询,无需刷新 | `v_store_operating_expense_monthly`, `v_operating_expense_account_monthly` | 每次查询实时计算 |
| 物化表 (Table) | `INSERT/UPDATE/DELETE` | `mv_store_operating_expense_monthly` | 物理表,需手动写入 |
### 判断方法
```sql
-- 查看对象类型
SELECT relname, relkind
FROM pg_class
WHERE relname LIKE 'mv_%' OR relname LIKE 'v_%'
AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'analytics');
-- relkind 含义:
-- 'm' = 物化视图 (需要 REFRESH)
-- 'v' = 普通视图 (无需刷新)
-- 'r' = 普通表 (需要 INSERT/UPDATE)
```
---
## 五、常见问题
### 5.1 数据不一致
**症状**: API返回的门店数少于实际门店数,或实收合计与 bill_fact 不一致
**原因**: 导入新数据后未刷新物化视图
**修复**: 执行上述刷新命令
### 5.2 费用未纳入门店评估
**症状**: 某门店在评估列表中缺失或费用为0
**原因**:
1. `operating_expense_store_mapping``sales_store_code` 未设置
2. `include_in_operating_analysis` 标志为 false
3. `mv_store_operating_expense_monthly` 物化表未更新
**修复**:
```sql
-- 检查映射
SELECT * FROM operating_expense_store_mapping WHERE cost_unit_source_name ILIKE '%门店名%';
-- 更新映射
UPDATE operating_expense_store_mapping
SET sales_store_code = '0022', include_in_operating_analysis = true
WHERE cost_unit_source_name = '哈马尔罕大钟寺店';
-- 刷新物化表
DELETE FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '2026-04-01';
INSERT INTO analytics.mv_store_operating_expense_monthly
SELECT * FROM analytics.v_store_operating_expense_monthly WHERE report_month = '2026-04-01';
```
### 5.3 费用重复行
**症状**: 同一门店在 `mv_store_operating_expense_monthly` 中出现多行
**原因**: 多个费用单位映射到同一 `sales_store_code`,但视图未按 `sales_store_code` 聚合
**修复**: 确认 `v_store_operating_expense_monthly` 视图定义包含 `expense_agg` CTE,按 `sales_store_code` GROUP BY
### 5.4 固定月份物化视图
**问题**: `*_april` 系列物化视图硬编码2026年4月,不支持其他月份
**影响**: 5月数据需要创建新的 `*_may` 系列物化视图,或修改为通用月份
**当前状态**: `v_store_operating_expense_monthly` 视图中 sales/food/loc CTE 引用了 `v_store_site_profile_april``v_store_theoretical_actual_cost_april`,这些是4月专用视图
---
## 六、数据一致性验证
刷新后执行以下验证:
```sql
-- 1. 门店数一致性
SELECT 'bill_fact' AS src, count(DISTINCT store_code) AS stores
FROM analytics.bill_fact
WHERE closed_at >= '2026-04-01' AND closed_at < '2026-05-01'
UNION ALL
SELECT 'risk_rating', count(*)
FROM analytics.mv_store_risk_rating_monthly WHERE month_start = '2026-04-01'
UNION ALL
SELECT 'theo_cost', count(*)
FROM analytics.mv_store_theoretical_actual_cost_monthly WHERE month_start = '2026-04-01'
UNION ALL
SELECT 'op_expense', count(*)
FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '2026-04-01';
-- 2. 实收一致性
SELECT 'bill_fact' AS src, round(sum(received_total)::numeric,2) AS received
FROM analytics.bill_fact
WHERE closed_at >= '2026-04-01' AND closed_at < '2026-05-01'
UNION ALL
SELECT 'risk_rating', round(sum(received)::numeric,2)
FROM analytics.mv_store_risk_rating_monthly WHERE month_start = '2026-04-01'
UNION ALL
SELECT 'theo_cost', round(sum(sales_received)::numeric,2)
FROM analytics.mv_store_theoretical_actual_cost_monthly WHERE month_start = '2026-04-01'
UNION ALL
SELECT 'op_expense', round(sum(received)::numeric,2)
FROM analytics.mv_store_operating_expense_monthly WHERE report_month = '2026-04-01';
-- 3. 差异门店排查
SELECT store_code, store_name, received
FROM analytics.mv_store_risk_rating_monthly
WHERE month_start = '2026-04-01' AND received > 0
AND store_code NOT IN (
SELECT DISTINCT store_code FROM analytics.mv_store_operating_expense_monthly
WHERE report_month = '2026-04-01'
);
```