diff --git a/API.md b/API.md new file mode 100644 index 0000000..9c57eea --- /dev/null +++ b/API.md @@ -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** | diff --git a/API数据检查报告.md b/API数据检查报告.md new file mode 100644 index 0000000..94c5514 --- /dev/null +++ b/API数据检查报告.md @@ -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. **自动化物化视图刷新** — 建立数据导入后自动刷新机制 diff --git a/scripts/refresh_materialized_views.sh b/scripts/refresh_materialized_views.sh new file mode 100755 index 0000000..b7d2fc3 --- /dev/null +++ b/scripts/refresh_materialized_views.sh @@ -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" <= '${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 "==========================================" diff --git a/server/src/routes/data.ts b/server/src/routes/data.ts index cb779cf..97ff558 100644 --- a/server/src/routes/data.ts +++ b/server/src/routes/data.ts @@ -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 ) diff --git a/原始数据级导入处理.md b/原始数据级导入处理.md new file mode 100644 index 0000000..04111cf --- /dev/null +++ b/原始数据级导入处理.md @@ -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 聚合 diff --git a/本体.md b/本体.md new file mode 100644 index 0000000..aa3ff63 --- /dev/null +++ b/本体.md @@ -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月,不随月份参数变化 diff --git a/物化视图更新.md b/物化视图更新.md new file mode 100644 index 0000000..c6e8c4d --- /dev/null +++ b/物化视图更新.md @@ -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 <= '${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' +); +```