perf: 全量物化视图优化 — 按月预计算所有慢函数,API响应从106秒降至0.09秒

- 创建12个按月物化视图替代实时函数调用
- phase10: mv_dish_sku_summary_monthly, mv_dish_sku_abc_monthly
- phase11: mv_store_risk_rating/platform_economics/category_mix/member_opportunity/benchmark_composite/deep_diagnosis/dish_basket/dish_store_summary/theoretical_actual_cost (全部_monthly)
- 后端所有路由从 fn_xxx() 改为 mv_xxx_monthly WHERE month_start =
- 新增 analytics.refresh_all_monthly_views() 刷新函数
- 优化 fn_dish_sku_summary: CTE物化避免重复调用
- 优化 fn_dish_sku_abc: CTE物化 fn_dish_sku_summary 只调用一次
- 优化 fn_dish_sales: 添加 dish_name IS NOT NULL 提前过滤
This commit is contained in:
freedakgmail
2026-08-01 14:05:12 +08:00
parent dab06ba109
commit 25db0f3854
22 changed files with 2761 additions and 71 deletions
+7 -5
View File
@@ -734,7 +734,8 @@ router.get('/store-overview', async (req: AuthRequest, res) => {
count(*) FILTER (WHERE variance_level = '绿色-基本正常') AS green_count,
count(*) FILTER (WHERE variance_level = '灰色-口径异常') AS gray_count,
round(sum(food_cost_variance)::numeric, 2) AS total_variance
FROM analytics.fn_store_theoretical_actual_cost($1)
FROM analytics.mv_store_theoretical_actual_cost_monthly
WHERE month_start = $1
`, [month])
sendSuccess(res, result.rows[0])
} catch (err: any) {
@@ -752,9 +753,9 @@ router.get('/store-map', async (req: AuthRequest, res) => {
round(c.theoretical_cost_rate_pct::numeric, 2) AS theo_cost_rate,
round(c.actual_food_cost_rate_pct::numeric, 2) AS actual_cost_rate,
l.latitude_gcj02, l.longitude_gcj02
FROM analytics.fn_store_theoretical_actual_cost($1) c
FROM analytics.mv_store_theoretical_actual_cost_monthly c
LEFT JOIN analytics.v_store_location_operating l ON l.store_code = c.store_code
WHERE l.latitude_gcj02 IS NOT NULL
WHERE c.month_start = $1 AND l.latitude_gcj02 IS NOT NULL
`, [month])
sendSuccess(res, result.rows)
} catch (err: any) {
@@ -780,7 +781,7 @@ router.get('/store-ranking', async (req: AuthRequest, res) => {
const sortCol = validSorts.includes(sort) ? sort : 'food_cost_variance'
const month = parseMonth(req)
const countResult = await query(`SELECT count(*) FROM analytics.fn_store_theoretical_actual_cost($1) ${where}`, [month])
const countResult = await query(`SELECT count(*) FROM analytics.mv_store_theoretical_actual_cost_monthly WHERE month_start = $1 ${where}`, [month])
const total = countResult.rows[0].count
const result = await query(`
@@ -791,7 +792,8 @@ router.get('/store-ranking', async (req: AuthRequest, res) => {
round(food_cost_variance::numeric, 2) AS variance_amount,
negative_item_lines,
variance_level
FROM analytics.fn_store_theoretical_actual_cost($1)
FROM analytics.mv_store_theoretical_actual_cost_monthly
WHERE month_start = $1
${where}
ORDER BY ${sortCol} ${order} NULLS LAST LIMIT $2 OFFSET $3
`, [month, pageSize, offset])