perf: 物化视图加速门店详情和健康分API - 29秒降到毫秒级
This commit is contained in:
@@ -733,7 +733,7 @@ router.get('/store-overview', async (req: AuthRequest, res) => {
|
|||||||
count(*) FILTER (WHERE variance_level = '绿色-基本正常') AS green_count,
|
count(*) FILTER (WHERE variance_level = '绿色-基本正常') AS green_count,
|
||||||
count(*) FILTER (WHERE variance_level = '灰色-口径异常') AS gray_count,
|
count(*) FILTER (WHERE variance_level = '灰色-口径异常') AS gray_count,
|
||||||
round(sum(food_cost_variance)::numeric, 2) AS total_variance
|
round(sum(food_cost_variance)::numeric, 2) AS total_variance
|
||||||
FROM analytics.v_store_theoretical_actual_cost_april
|
FROM analytics.mv_store_theoretical_actual_cost_april
|
||||||
`)
|
`)
|
||||||
sendSuccess(res, result.rows[0])
|
sendSuccess(res, result.rows[0])
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -750,7 +750,7 @@ router.get('/store-map', async (req: AuthRequest, res) => {
|
|||||||
round(c.theoretical_cost_rate_pct::numeric, 2) AS theo_cost_rate,
|
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,
|
round(c.actual_food_cost_rate_pct::numeric, 2) AS actual_cost_rate,
|
||||||
l.latitude_gcj02, l.longitude_gcj02
|
l.latitude_gcj02, l.longitude_gcj02
|
||||||
FROM analytics.v_store_theoretical_actual_cost_april c
|
FROM analytics.mv_store_theoretical_actual_cost_april c
|
||||||
LEFT JOIN analytics.v_store_location_operating l ON l.store_code = c.store_code
|
LEFT JOIN analytics.v_store_location_operating l ON l.store_code = c.store_code
|
||||||
WHERE l.latitude_gcj02 IS NOT NULL
|
WHERE l.latitude_gcj02 IS NOT NULL
|
||||||
`)
|
`)
|
||||||
@@ -777,7 +777,7 @@ router.get('/store-ranking', async (req: AuthRequest, res) => {
|
|||||||
const validSorts = ['food_cost_variance', 'theoretical_cost_rate_pct', 'actual_food_cost_rate_pct', 'variance_to_theoretical_pct', 'negative_item_lines', 'store_name']
|
const validSorts = ['food_cost_variance', 'theoretical_cost_rate_pct', 'actual_food_cost_rate_pct', 'variance_to_theoretical_pct', 'negative_item_lines', 'store_name']
|
||||||
const sortCol = validSorts.includes(sort) ? sort : 'food_cost_variance'
|
const sortCol = validSorts.includes(sort) ? sort : 'food_cost_variance'
|
||||||
|
|
||||||
const countResult = await query(`SELECT count(*) FROM analytics.v_store_theoretical_actual_cost_april ${where}`)
|
const countResult = await query(`SELECT count(*) FROM analytics.mv_store_theoretical_actual_cost_april ${where}`)
|
||||||
const total = countResult.rows[0].count
|
const total = countResult.rows[0].count
|
||||||
|
|
||||||
const result = await query(`
|
const result = await query(`
|
||||||
@@ -788,7 +788,7 @@ router.get('/store-ranking', async (req: AuthRequest, res) => {
|
|||||||
round(food_cost_variance::numeric, 2) AS variance_amount,
|
round(food_cost_variance::numeric, 2) AS variance_amount,
|
||||||
negative_item_lines,
|
negative_item_lines,
|
||||||
variance_level
|
variance_level
|
||||||
FROM analytics.v_store_theoretical_actual_cost_april
|
FROM analytics.mv_store_theoretical_actual_cost_april
|
||||||
${where}
|
${where}
|
||||||
ORDER BY ${sortCol} ${order} NULLS LAST LIMIT $1 OFFSET $2
|
ORDER BY ${sortCol} ${order} NULLS LAST LIMIT $1 OFFSET $2
|
||||||
`, [pageSize, offset])
|
`, [pageSize, offset])
|
||||||
|
|||||||
@@ -109,11 +109,11 @@ router.get('/stores/:code', async (req: AuthRequest, res) => {
|
|||||||
try {
|
try {
|
||||||
const code = req.params.code
|
const code = req.params.code
|
||||||
const [scorecard, risk, platform, benchmark, action] = await Promise.all([
|
const [scorecard, risk, platform, benchmark, action] = await Promise.all([
|
||||||
query(`SELECT * FROM analytics.v_store_scorecard WHERE store_code = $1`, [code]),
|
query(`SELECT * FROM analytics.mv_store_scorecard WHERE store_code = $1`, [code]),
|
||||||
query(`SELECT * FROM analytics.mv_store_risk_rating WHERE store_code = $1`, [code]),
|
query(`SELECT * FROM analytics.mv_store_risk_rating WHERE store_code = $1`, [code]),
|
||||||
query(`SELECT * FROM analytics.v_store_platform_economics WHERE store_code = $1`, [code]),
|
query(`SELECT * FROM analytics.v_store_platform_economics WHERE store_code = $1`, [code]),
|
||||||
query(`SELECT * FROM analytics.v_store_benchmark_composite WHERE store_code = $1`, [code]),
|
query(`SELECT * FROM analytics.mv_store_benchmark_composite WHERE store_code = $1`, [code]),
|
||||||
query(`SELECT * FROM analytics.v_store_action_priority_deep_april WHERE store_code = $1`, [code]),
|
query(`SELECT * FROM analytics.mv_store_action_priority_deep_april WHERE store_code = $1`, [code]),
|
||||||
])
|
])
|
||||||
|
|
||||||
if (scorecard.rows.length === 0) {
|
if (scorecard.rows.length === 0) {
|
||||||
@@ -172,7 +172,7 @@ router.get('/stores/:code/daily', async (req: AuthRequest, res) => {
|
|||||||
|
|
||||||
router.get('/cost/comparison', async (req: AuthRequest, res) => {
|
router.get('/cost/comparison', async (req: AuthRequest, res) => {
|
||||||
try {
|
try {
|
||||||
const result = await query(`SELECT * FROM analytics.v_store_theoretical_actual_cost_april ORDER BY variance_to_theoretical_pct DESC NULLS LAST`)
|
const result = await query(`SELECT * FROM analytics.mv_store_theoretical_actual_cost_april ORDER BY variance_to_theoretical_pct DESC NULLS LAST`)
|
||||||
sendSuccess(res, result.rows)
|
sendSuccess(res, result.rows)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
sendError(res, err.message)
|
sendError(res, err.message)
|
||||||
@@ -394,7 +394,7 @@ router.get('/stores/:code/category-mix', async (req: AuthRequest, res) => {
|
|||||||
router.get('/stores/:code/cost', async (req: AuthRequest, res) => {
|
router.get('/stores/:code/cost', async (req: AuthRequest, res) => {
|
||||||
try {
|
try {
|
||||||
const [costResult, catResult] = await Promise.all([
|
const [costResult, catResult] = await Promise.all([
|
||||||
query(`SELECT * FROM analytics.v_store_theoretical_actual_cost_april WHERE store_code = $1`, [req.params.code]),
|
query(`SELECT * FROM analytics.mv_store_theoretical_actual_cost_april WHERE store_code = $1`, [req.params.code]),
|
||||||
query(`
|
query(`
|
||||||
WITH store_cat AS (
|
WITH store_cat AS (
|
||||||
SELECT finance_category AS category_name,
|
SELECT finance_category AS category_name,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ router.get('/health-score', async (req: AuthRequest, res) => {
|
|||||||
cost AS (
|
cost AS (
|
||||||
SELECT store_code,
|
SELECT store_code,
|
||||||
round(avg(LEAST(variance_to_theoretical_pct, 50))::numeric, 1) AS avg_cost_variance_pct
|
round(avg(LEAST(variance_to_theoretical_pct, 50))::numeric, 1) AS avg_cost_variance_pct
|
||||||
FROM analytics.v_store_theoretical_actual_cost_april
|
FROM analytics.mv_store_theoretical_actual_cost_april
|
||||||
WHERE variance_to_theoretical_pct IS NOT NULL
|
WHERE variance_to_theoretical_pct IS NOT NULL
|
||||||
GROUP BY store_code
|
GROUP BY store_code
|
||||||
),
|
),
|
||||||
@@ -292,7 +292,7 @@ router.get('/correlation', async (req: AuthRequest, res) => {
|
|||||||
LEFT JOIN (
|
LEFT JOIN (
|
||||||
SELECT store_code,
|
SELECT store_code,
|
||||||
round(avg(LEAST(variance_to_theoretical_pct, 50))::numeric, 1) AS avg_cost_variance_pct
|
round(avg(LEAST(variance_to_theoretical_pct, 50))::numeric, 1) AS avg_cost_variance_pct
|
||||||
FROM analytics.v_store_theoretical_actual_cost_april
|
FROM analytics.mv_store_theoretical_actual_cost_april
|
||||||
WHERE variance_to_theoretical_pct IS NOT NULL
|
WHERE variance_to_theoretical_pct IS NOT NULL
|
||||||
GROUP BY store_code
|
GROUP BY store_code
|
||||||
) c ON r.store_code = c.store_code
|
) c ON r.store_code = c.store_code
|
||||||
|
|||||||
Reference in New Issue
Block a user