风险与内控管理优化: 异常账单服务端分页/筛选/收银员搜索/合计, 收银员TOP15按异常账单数排序, 双击跳转明细, MonthPicker靠右, 侧边栏底部间距
This commit is contained in:
@@ -123,8 +123,15 @@ router.get('/monthly-review', async (req: AuthRequest, res) => {
|
||||
|
||||
router.get('/followup', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const result = await query(`SELECT * FROM analytics.mv_store_monthly_followup ORDER BY priority, store_code`)
|
||||
sendSuccess(res, result.rows)
|
||||
const month = req.query.month as string | undefined
|
||||
if (month) {
|
||||
const monthDate = month + '-01'
|
||||
const result = await query(`SELECT * FROM analytics.mv_store_monthly_followup WHERE plan_month = $1::date ORDER BY priority, store_code`, [monthDate])
|
||||
sendSuccess(res, result.rows)
|
||||
} else {
|
||||
const result = await query(`SELECT * FROM analytics.mv_store_monthly_followup ORDER BY priority, store_code`)
|
||||
sendSuccess(res, result.rows)
|
||||
}
|
||||
} catch (err: any) {
|
||||
sendError(res, err.message)
|
||||
}
|
||||
@@ -132,7 +139,8 @@ router.get('/followup', async (req: AuthRequest, res) => {
|
||||
|
||||
router.get('/grade-change', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const result = await query(`SELECT * FROM analytics.store_grade_change ORDER BY change_month DESC, store_code`)
|
||||
const month = parseMonth(req)
|
||||
const result = await query(`SELECT * FROM analytics.store_grade_change WHERE change_month = $1::date ORDER BY change_month DESC, store_code`, [month])
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) {
|
||||
sendError(res, err.message)
|
||||
@@ -278,7 +286,49 @@ router.post('/notifications/dispatch', async (req: AuthRequest, res) => {
|
||||
router.get('/stores/:code/daily-card', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const code = req.params.code
|
||||
const result = await query(`SELECT * FROM analytics.v_store_daily_card WHERE store_code = $1 ORDER BY module`, [code])
|
||||
const month = req.query.month as string | undefined
|
||||
let dateFilter: string
|
||||
let params: any[] = [code]
|
||||
if (month) {
|
||||
const monthDate = month + '-01'
|
||||
dateFilter = `closed_at::date = (SELECT max(closed_at)::date FROM analytics.fact_bill WHERE closed_at >= $2::date AND closed_at < ($2::date + interval '1 month') AND closed_at IS NOT NULL)`
|
||||
params.push(monthDate)
|
||||
} else {
|
||||
dateFilter = `closed_at::date = (SELECT max(closed_at)::date FROM analytics.fact_bill WHERE closed_at IS NOT NULL)`
|
||||
}
|
||||
const result = await query(`
|
||||
WITH target_day AS (
|
||||
SELECT max(closed_at)::date AS business_date
|
||||
FROM analytics.fact_bill
|
||||
WHERE ${dateFilter.replace('closed_at', 'fact_bill.closed_at')}
|
||||
),
|
||||
today_stats AS (
|
||||
SELECT bf.store_code, bf.store_name,
|
||||
round(sum(bf.received_total), 2) AS received,
|
||||
count(*) AS bill_count,
|
||||
round(sum(bf.received_total) / count(*), 2) AS avg_bill
|
||||
FROM analytics.fact_bill bf, target_day td
|
||||
WHERE bf.store_code = $1 AND bf.closed_at::date = td.business_date
|
||||
GROUP BY bf.store_code, bf.store_name
|
||||
),
|
||||
week_ago_stats AS (
|
||||
SELECT bf.store_code,
|
||||
round(sum(bf.received_total), 2) AS received,
|
||||
count(*) AS bill_count,
|
||||
round(sum(bf.received_total) / count(*), 2) AS avg_bill
|
||||
FROM analytics.fact_bill bf, target_day td
|
||||
WHERE bf.store_code = $1 AND bf.closed_at::date = td.business_date - interval '7 days'
|
||||
GROUP BY bf.store_code
|
||||
)
|
||||
SELECT t.store_code, t.store_name, '收入' AS module,
|
||||
jsonb_build_array(
|
||||
jsonb_build_object('metric', '实收', 'value', t.received, 'baseline', COALESCE(w.received, 0), 'is_anomaly', t.received < (COALESCE(w.received, 0) * 0.8)),
|
||||
jsonb_build_object('metric', '账单数', 'value', t.bill_count, 'baseline', COALESCE(w.bill_count, 0), 'is_anomaly', t.bill_count::numeric < (COALESCE(w.bill_count, 0) * 0.8)),
|
||||
jsonb_build_object('metric', '客单价', 'value', t.avg_bill, 'baseline', COALESCE(w.avg_bill, 0), 'is_anomaly', t.avg_bill < (COALESCE(w.avg_bill, 0) * 0.9))
|
||||
) AS anomalies
|
||||
FROM today_stats t
|
||||
LEFT JOIN week_ago_stats w ON t.store_code = w.store_code
|
||||
`, params)
|
||||
const tasks = await query(`
|
||||
SELECT t.* FROM analytics.store_task t
|
||||
WHERE t.store_code = $1 AND t.status IN ('待启动', '进行中')
|
||||
@@ -601,14 +651,28 @@ router.get('/monthly-review/completion', async (req: AuthRequest, res) => {
|
||||
|
||||
router.get('/monthly-review/activity-list', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const month = parseMonth(req)
|
||||
const result = await query(`
|
||||
SELECT marketing_plan, bill_count, consumption, discounts, received,
|
||||
SELECT marketing_plan, bill_count, consumption, discounts, received,
|
||||
avg_bill_value, discount_rate_pct, theoretical_margin_pct,
|
||||
ROUND(discounts * 100.0 / NULLIF(consumption, 0), 2) as discount_cost_rate,
|
||||
ROUND(received - discounts, 2) as net_contribution
|
||||
FROM analytics.v_marketing_plan_summary
|
||||
FROM (
|
||||
SELECT marketing_plan,
|
||||
count(*) AS bill_count,
|
||||
sum(consumption) AS consumption,
|
||||
sum(discount_total) AS discounts,
|
||||
sum(received_total) AS received,
|
||||
round(avg(received_total), 2) AS avg_bill_value,
|
||||
round(sum(discount_total) / NULLIF(sum(consumption), 0) * 100, 2) AS discount_rate_pct,
|
||||
round(sum(theoretical_profit) / NULLIF(sum(received_total), 0) * 100, 2) AS theoretical_margin_pct
|
||||
FROM analytics.fact_bill
|
||||
WHERE marketing_plan IS NOT NULL
|
||||
AND closed_at >= $1::date AND closed_at < ($1::date + interval '1 month')
|
||||
GROUP BY marketing_plan
|
||||
) t
|
||||
ORDER BY received DESC
|
||||
`)
|
||||
`, [month])
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) { sendError(res, err.message) }
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user