Phase 6-11: 驾驶舱图表/区域经理基准对比+周检录入/店长SKU备货+指标进度/门店详情7Tab/月度验收7Tab/区域汇总
- Phase 6: 驾驶舱新增象限散点图、P0/P1实收覆盖瀑布图、平台成本率TOP15柱状图 - Phase 7: 区域经理页面新增同类门店基准对比表、周检录入弹窗 - Phase 8: 店长页面新增核心SKU备货提醒、本周指标进度条,经营概览与进度并排 - Phase 9: 门店详情页7Tab(概览/餐段/品类/成本/会员/异常/任务),5个新API - Phase 10: 月度验收7Tab(验收汇总/升降级/完成率/活动清单/SKU治理/经验标准化/复盘优化),4个新API+阈值调整API - Phase 11: 前后端编译通过,API测试通过,浏览器验证0 error - 门店区域划分:从store_location_master填充dim_store.region(87家),v_region_summary视图+API+前端表格 - MetricCard增加text格式支持
This commit is contained in:
@@ -373,4 +373,73 @@ router.get('/data-quality', async (req: AuthRequest, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
// ============================================================
|
||||
// 门店详情页扩展 API (Phase 9)
|
||||
// ============================================================
|
||||
|
||||
router.get('/stores/:code/meal-period', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const result = await query(`SELECT * FROM analytics.v_store_meal_opportunity WHERE store_code = $1 ORDER BY bill_count DESC`, [req.params.code])
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) { sendError(res, err.message) }
|
||||
})
|
||||
|
||||
router.get('/stores/:code/category-mix', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const result = await query(`SELECT * FROM analytics.v_store_category_mix WHERE store_code = $1`, [req.params.code])
|
||||
const companyResult = await query(`
|
||||
SELECT
|
||||
SUM(consumption) as total_consumption,
|
||||
SUM(lanzhou_noodle) as total_noodle,
|
||||
SUM(western_staple) as total_western,
|
||||
SUM(delivery_package) as total_delivery,
|
||||
SUM(cold_dishes) as total_cold,
|
||||
SUM(silk_road_food) as total_silk
|
||||
FROM analytics.v_store_category_mix
|
||||
`)
|
||||
sendSuccess(res, { store: result.rows[0], company: companyResult.rows[0] })
|
||||
} catch (err: any) { sendError(res, err.message) }
|
||||
})
|
||||
|
||||
router.get('/stores/:code/cost', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const costResult = await query(`SELECT * FROM analytics.v_store_theoretical_actual_cost_april WHERE store_code = $1`, [req.params.code])
|
||||
const catResult = await query(`SELECT * FROM analytics.v_store_category_cost_benchmark_april WHERE store_code = $1`, [req.params.code])
|
||||
sendSuccess(res, { cost: costResult.rows[0], categories: catResult.rows })
|
||||
} catch (err: any) { sendError(res, err.message) }
|
||||
})
|
||||
|
||||
router.get('/stores/:code/member', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const oppResult = await query(`SELECT * FROM analytics.v_store_member_opportunity WHERE store_code = $1`, [req.params.code])
|
||||
const repeatResult = await query(`SELECT * FROM analytics.v_store_repeat_summary_monthly WHERE store_code = $1`, [req.params.code])
|
||||
const monthlyResult = await query(`
|
||||
SELECT store_code, store_name, count(*) as member_count,
|
||||
sum(orders) as total_orders, sum(received) as total_received,
|
||||
avg(orders) as avg_orders, avg(received) as avg_received
|
||||
FROM analytics.v_store_member_monthly_activity
|
||||
WHERE store_code = $1
|
||||
GROUP BY store_code, store_name
|
||||
`, [req.params.code])
|
||||
sendSuccess(res, { opportunity: oppResult.rows[0], repeat: repeatResult.rows[0], monthly: monthlyResult.rows[0] })
|
||||
} catch (err: any) { sendError(res, err.message) }
|
||||
})
|
||||
|
||||
router.get('/stores/:code/anomalies', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const { page, pageSize, offset } = parsePagination(req)
|
||||
const countResult = await query(`SELECT count(*) AS total FROM analytics.v_anomaly_bills WHERE store_code = $1`, [req.params.code])
|
||||
const result = await query(`SELECT * FROM analytics.v_anomaly_bills WHERE store_code = $1 ORDER BY closed_at DESC LIMIT $2 OFFSET $3`, [req.params.code, pageSize, offset])
|
||||
sendSuccess(res, result.rows, { total: parseInt(countResult.rows[0].total), page, page_size: pageSize })
|
||||
} catch (err: any) { sendError(res, err.message) }
|
||||
})
|
||||
|
||||
// 区域汇总
|
||||
router.get('/region/summary', async (req, res) => {
|
||||
try {
|
||||
const result = await query(`SELECT * FROM analytics.v_region_summary ORDER BY total_received DESC`)
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) { sendError(res, err.message) }
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
Reference in New Issue
Block a user