feat: 整合菜单7组 + 新增营收分析页面(渠道/时段/门店排名/日度趋势)
This commit is contained in:
@@ -564,4 +564,100 @@ router.get('/overview/store-profit-ranking', async (req: AuthRequest, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
// ============ 营收分析 ============
|
||||
|
||||
// 渠道收入结构
|
||||
router.get('/revenue/channel', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const result = await query(`
|
||||
SELECT
|
||||
business_date,
|
||||
round(cash::numeric, 2) AS cash,
|
||||
round(alipay::numeric, 2) AS alipay,
|
||||
round(wechat::numeric, 2) AS wechat,
|
||||
round(meituan::numeric, 2) AS meituan,
|
||||
round(unionpay::numeric, 2) AS unionpay,
|
||||
round(douyin::numeric, 2) AS douyin,
|
||||
round(credit::numeric, 2) AS credit,
|
||||
round(jd_delivery::numeric, 2) AS jd_delivery,
|
||||
round(meituan_delivery::numeric, 2) AS meituan_delivery,
|
||||
round(taobao_delivery::numeric, 2) AS taobao_delivery
|
||||
FROM analytics.v_channel_daily
|
||||
WHERE business_date >= DATE '2026-04-01'
|
||||
ORDER BY business_date
|
||||
`)
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) {
|
||||
sendError(res, err.message)
|
||||
}
|
||||
})
|
||||
|
||||
// 时段收入分布
|
||||
router.get('/revenue/meal-period', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const result = await query(`
|
||||
SELECT
|
||||
meal_period,
|
||||
sum(bill_count)::bigint AS bill_count,
|
||||
round(sum(received)::numeric, 2) AS received,
|
||||
round(avg(avg_bill_value)::numeric, 2) AS avg_bill_value
|
||||
FROM analytics.v_meal_period_daily
|
||||
WHERE business_date >= DATE '2026-04-01'
|
||||
GROUP BY meal_period
|
||||
ORDER BY sum(received) DESC
|
||||
`)
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) {
|
||||
sendError(res, err.message)
|
||||
}
|
||||
})
|
||||
|
||||
// 门店营收排名
|
||||
router.get('/revenue/store-ranking', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const result = await query(`
|
||||
SELECT
|
||||
store_code,
|
||||
store_name,
|
||||
sum(bill_count)::bigint AS bill_count,
|
||||
round(sum(received)::numeric, 2) AS received,
|
||||
round(sum(discounts)::numeric, 2) AS discounts,
|
||||
round(avg(discount_rate_pct)::numeric, 2) AS avg_discount_rate_pct,
|
||||
round(avg(avg_bill_value)::numeric, 2) AS avg_bill_value,
|
||||
round(sum(guests)::numeric, 0) AS guests,
|
||||
round(avg(theoretical_margin_pct)::numeric, 2) AS avg_theoretical_margin_pct
|
||||
FROM analytics.v_store_daily
|
||||
WHERE business_date >= DATE '2026-04-01'
|
||||
GROUP BY store_code, store_name
|
||||
ORDER BY sum(received) DESC
|
||||
`)
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) {
|
||||
sendError(res, err.message)
|
||||
}
|
||||
})
|
||||
|
||||
// 日度营收汇总
|
||||
router.get('/revenue/daily-summary', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const result = await query(`
|
||||
SELECT
|
||||
business_date,
|
||||
sum(bill_count)::bigint AS bill_count,
|
||||
round(sum(received)::numeric, 2) AS received,
|
||||
round(sum(discounts)::numeric, 2) AS discounts,
|
||||
round(avg(discount_rate_pct)::numeric, 2) AS avg_discount_rate_pct,
|
||||
round(avg(avg_bill_value)::numeric, 2) AS avg_bill_value,
|
||||
round(sum(guests)::numeric, 0) AS guests
|
||||
FROM analytics.v_store_daily
|
||||
WHERE business_date >= DATE '2026-04-01'
|
||||
GROUP BY business_date
|
||||
ORDER BY business_date
|
||||
`)
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) {
|
||||
sendError(res, err.message)
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
Reference in New Issue
Block a user