perf: Phase 12 — 物化剩余9个慢函数,全量API优化完成

新增物化视图:
- mv_store_action_priority_deep_monthly (10.7s→3ms)
- mv_inventory_cost_classified_monthly (37ms→3ms, 消除重复调用)
- mv_dish_pair_summary_monthly (171s→3ms)
- mv_store_area_efficiency_monthly (10.1s→3ms)
- mv_store_site_profile_monthly (9.3s→3ms)
- mv_site_segment_benchmark_monthly (12s→3ms)
- mv_store_site_replication_monthly (12.6s→3ms)
- mv_store_overlap_risk_monthly (88s→3ms)
- mv_district_site_benchmark_monthly (56s→3ms)

后端: 所有 fn_ 函数调用替换为物化视图查询
前端: BossPage 添加 MonthPicker 组件并传递 month 参数
修复: cost/category-benchmark 排序列名 store_code→sales_store_code
总计: 21个物化视图, 全部API响应<1.7秒
This commit is contained in:
freedakgmail
2026-08-01 14:51:24 +08:00
parent 25db0f3854
commit 122290e3b0
8 changed files with 773 additions and 39 deletions
+15 -14
View File
@@ -116,7 +116,7 @@ router.get('/stores/:code', async (req: AuthRequest, res) => {
query(`SELECT * FROM analytics.mv_store_risk_rating_monthly WHERE month_start = $1 AND store_code = $2`, [parseMonth(req), code]),
query(`SELECT * FROM analytics.mv_store_platform_economics_monthly WHERE month_start = $2 AND store_code = $1`, [code, parseMonth(req)]),
query(`SELECT * FROM analytics.mv_store_benchmark_composite_monthly WHERE month_start = $2 AND store_code = $1`, [code, parseMonth(req)]),
query(`SELECT * FROM analytics.fn_store_action_priority_deep($1) WHERE store_code = $2`, [parseMonth(req), code]),
query(`SELECT * FROM analytics.mv_store_action_priority_deep_monthly WHERE month_start = $1 AND store_code = $2`, [parseMonth(req), code]),
])
if (scorecard.rows.length === 0) {
@@ -186,7 +186,7 @@ router.get('/cost/comparison', async (req: AuthRequest, res) => {
router.get('/cost/category-benchmark', async (req: AuthRequest, res) => {
try {
const month = parseMonth(req)
const result = await query(`SELECT * FROM analytics.fn_inventory_finance_category($1) ORDER BY store_code`, [month])
const result = await query(`SELECT * FROM analytics.mv_inventory_cost_classified_monthly WHERE month_start = $1 ORDER BY sales_store_code`, [month])
sendSuccess(res, result.rows)
} catch (err: any) {
sendError(res, err.message)
@@ -196,7 +196,7 @@ router.get('/cost/category-benchmark', async (req: AuthRequest, res) => {
router.get('/cost/inventory', async (req: AuthRequest, res) => {
try {
const month = parseMonth(req)
const result = await query(`SELECT * FROM analytics.fn_store_area_efficiency($1) ORDER BY estimated_inventory_days DESC NULLS LAST`, [month])
const result = await query(`SELECT * FROM analytics.mv_store_area_efficiency_monthly WHERE month_start = $1 ORDER BY estimated_inventory_days DESC NULLS LAST`, [month])
sendSuccess(res, result.rows)
} catch (err: any) {
sendError(res, err.message)
@@ -254,7 +254,7 @@ router.get('/sku/category', async (req: AuthRequest, res) => {
router.get('/sku/attach', async (req: AuthRequest, res) => {
try {
const month = parseMonth(req)
const result = await query(`SELECT * FROM analytics.fn_dish_pair_summary($1) ORDER BY pair_count DESC LIMIT 50`, [month])
const result = await query(`SELECT * FROM analytics.mv_dish_pair_summary_monthly WHERE month_start = $1 ORDER BY pair_count DESC LIMIT 50`, [month])
sendSuccess(res, result.rows)
} catch (err: any) {
sendError(res, err.message)
@@ -372,7 +372,8 @@ router.get('/data-quality', async (req: AuthRequest, res) => {
SELECT
count(*) FILTER (WHERE consumption_amount < 0) AS negative_consumption_count,
round(sum(consumption_amount) FILTER (WHERE consumption_amount < 0)::numeric, 2) AS negative_consumption_amount
FROM analytics.fn_inventory_cost_classified($1)
FROM analytics.mv_inventory_cost_classified_monthly
WHERE month_start = $1
`, [month])
const result = { ...billStats.rows[0], ...dishStats.rows[0], ...inventoryStats.rows[0] }
sendSuccess(res, result)
@@ -422,16 +423,16 @@ router.get('/stores/:code/cost', async (req: AuthRequest, res) => {
SELECT finance_category AS category_name,
round(sum(consumption_amount)::numeric, 2) AS consumption_amount,
round(sum(consumption_amount)::numeric / nullif(sum(sum(consumption_amount)) OVER (), 0) * 100, 1) AS actual_cost_rate_pct
FROM analytics.fn_inventory_cost_classified($1)
WHERE sales_store_code = $2 AND finance_category IS NOT NULL
FROM analytics.mv_inventory_cost_classified_monthly
WHERE month_start = $1 AND sales_store_code = $2 AND finance_category IS NOT NULL
GROUP BY finance_category
ORDER BY sum(consumption_amount) DESC
),
company_cat AS (
SELECT finance_category,
round(sum(consumption_amount)::numeric / nullif(sum(sum(consumption_amount)) OVER (), 0) * 100, 1) AS benchmark_rate_pct
FROM analytics.fn_inventory_cost_classified($1)
WHERE finance_category IS NOT NULL
FROM analytics.mv_inventory_cost_classified_monthly
WHERE month_start = $1 AND finance_category IS NOT NULL
GROUP BY finance_category
)
SELECT s.category_name, s.consumption_amount, s.actual_cost_rate_pct,
@@ -486,7 +487,7 @@ router.get('/region/summary', async (req, res) => {
router.get('/site-selection/profile', async (req: AuthRequest, res) => {
try {
const month = parseMonth(req)
const result = await query(`SELECT * FROM analytics.fn_store_site_profile($1) WHERE business_type='标准门店' AND received>0 ORDER BY received DESC`, [month])
const result = await query(`SELECT * FROM analytics.mv_store_site_profile_monthly WHERE month_start = $1 AND business_type='标准门店' AND received>0 ORDER BY received DESC`, [month])
sendSuccess(res, result.rows)
} catch (err: any) { sendError(res, err.message) }
})
@@ -495,7 +496,7 @@ router.get('/site-selection/profile', async (req: AuthRequest, res) => {
router.get('/site-selection/segment-benchmark', async (req: AuthRequest, res) => {
try {
const month = parseMonth(req)
const result = await query(`SELECT * FROM analytics.fn_site_segment_benchmark($1) ORDER BY avg_received_per_sqm DESC`, [month])
const result = await query(`SELECT * FROM analytics.mv_site_segment_benchmark_monthly WHERE month_start = $1 ORDER BY avg_received_per_sqm DESC`, [month])
sendSuccess(res, result.rows)
} catch (err: any) { sendError(res, err.message) }
})
@@ -504,7 +505,7 @@ router.get('/site-selection/segment-benchmark', async (req: AuthRequest, res) =>
router.get('/site-selection/replication', async (req: AuthRequest, res) => {
try {
const month = parseMonth(req)
const result = await query(`SELECT * FROM analytics.fn_store_site_replication($1) ORDER BY site_replication_score DESC`, [month])
const result = await query(`SELECT * FROM analytics.mv_store_site_replication_monthly WHERE month_start = $1 ORDER BY site_replication_score DESC`, [month])
sendSuccess(res, result.rows)
} catch (err: any) { sendError(res, err.message) }
})
@@ -513,7 +514,7 @@ router.get('/site-selection/replication', async (req: AuthRequest, res) => {
router.get('/site-selection/overlap-risk', async (req: AuthRequest, res) => {
try {
const month = parseMonth(req)
const result = await query(`SELECT * FROM analytics.fn_store_overlap_risk($1) ORDER BY distance_km`, [month])
const result = await query(`SELECT * FROM analytics.mv_store_overlap_risk_monthly WHERE month_start = $1 ORDER BY distance_km`, [month])
sendSuccess(res, result.rows)
} catch (err: any) { sendError(res, err.message) }
})
@@ -522,7 +523,7 @@ router.get('/site-selection/overlap-risk', async (req: AuthRequest, res) => {
router.get('/site-selection/district-benchmark', async (req: AuthRequest, res) => {
try {
const month = parseMonth(req)
const result = await query(`SELECT * FROM analytics.fn_district_site_benchmark($1) ORDER BY total_received DESC`, [month])
const result = await query(`SELECT * FROM analytics.mv_district_site_benchmark_monthly WHERE month_start = $1 ORDER BY total_received DESC`, [month])
sendSuccess(res, result.rows)
} catch (err: any) { sendError(res, err.message) }
})