feat: 配送物化视图优化查询性能 + 饼图统一处理 + SKU长尾治理明细 + 地图初始聚焦北京
This commit is contained in:
+69
-26
@@ -1597,6 +1597,13 @@ router.get('/central-kitchen/dashboard', async (req: AuthRequest, res) => {
|
||||
router.get('/distribution/reconciliation', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const month = parseMonth(req)
|
||||
|
||||
// 检查配送物化视图是否包含当前月份数据
|
||||
const distMvCheck = await query(`SELECT 1 FROM mv_distribution_monthly WHERE report_month = $1::date LIMIT 1`, [month])
|
||||
if (distMvCheck.rows.length === 0) {
|
||||
await query(`REFRESH MATERIALIZED VIEW CONCURRENTLY mv_distribution_monthly`)
|
||||
}
|
||||
|
||||
const [summary, storeReconciliation, topVariances, unmatchedItems, categoryReconciliation] = await Promise.all([
|
||||
// 汇总
|
||||
query(`
|
||||
@@ -1605,9 +1612,8 @@ router.get('/distribution/reconciliation', async (req: AuthRequest, res) => {
|
||||
round(sum(d.total_quantity)::numeric,2) AS dist_qty,
|
||||
round(sum(d.outbound_total_amount)::numeric,2) AS dist_amt,
|
||||
round(sum(d.cost_excl_tax_amount)::numeric,2) AS dist_cost_excl_tax
|
||||
FROM distribution_detail_records d
|
||||
WHERE d.business_date >= $1::date AND d.business_date < ($1::date + INTERVAL '1 month')
|
||||
AND NOT d.is_return AND d.item_code IS NOT NULL
|
||||
FROM mv_distribution_monthly d
|
||||
WHERE d.report_month = $1::date
|
||||
GROUP BY d.store_code, d.item_code
|
||||
),
|
||||
inv AS (
|
||||
@@ -1658,9 +1664,8 @@ router.get('/distribution/reconciliation', async (req: AuthRequest, res) => {
|
||||
round(sum(d.total_quantity)::numeric,2) AS dist_qty,
|
||||
round(sum(d.outbound_total_amount)::numeric,2) AS dist_amt,
|
||||
round(sum(d.cost_excl_tax_amount)::numeric,2) AS dist_cost_excl_tax
|
||||
FROM distribution_detail_records d
|
||||
WHERE d.business_date >= $1::date AND d.business_date < ($1::date + INTERVAL '1 month')
|
||||
AND NOT d.is_return AND d.item_code IS NOT NULL
|
||||
FROM mv_distribution_monthly d
|
||||
WHERE d.report_month = $1::date
|
||||
GROUP BY d.store_code
|
||||
),
|
||||
inv AS (
|
||||
@@ -1674,8 +1679,8 @@ router.get('/distribution/reconciliation', async (req: AuthRequest, res) => {
|
||||
GROUP BY f.store_code
|
||||
),
|
||||
store_names AS (
|
||||
SELECT DISTINCT store_code, store_name FROM distribution_detail_records
|
||||
WHERE business_date >= $1::date AND business_date < ($1::date + INTERVAL '1 month')
|
||||
SELECT DISTINCT store_code, store_name FROM mv_distribution_monthly
|
||||
WHERE report_month = $1::date
|
||||
)
|
||||
SELECT COALESCE(d.store_code, i.store_code) AS store_code,
|
||||
sn.store_name,
|
||||
@@ -1699,9 +1704,8 @@ router.get('/distribution/reconciliation', async (req: AuthRequest, res) => {
|
||||
SELECT d.store_code, d.item_code, d.item_name, d.unit, d.major_category, d.minor_category,
|
||||
round(sum(d.total_quantity)::numeric,2) AS dist_qty,
|
||||
round(sum(d.outbound_total_amount)::numeric,2) AS dist_amt
|
||||
FROM distribution_detail_records d
|
||||
WHERE d.business_date >= $1::date AND d.business_date < ($1::date + INTERVAL '1 month')
|
||||
AND NOT d.is_return AND d.item_code IS NOT NULL
|
||||
FROM mv_distribution_monthly d
|
||||
WHERE d.report_month = $1::date
|
||||
GROUP BY d.store_code, d.item_code, d.item_name, d.unit, d.major_category, d.minor_category
|
||||
),
|
||||
inv AS (
|
||||
@@ -1733,9 +1737,8 @@ router.get('/distribution/reconciliation', async (req: AuthRequest, res) => {
|
||||
round(sum(d.total_quantity)::numeric,2) AS dist_qty,
|
||||
round(sum(d.outbound_total_amount)::numeric,2) AS dist_amt,
|
||||
count(DISTINCT d.store_code) AS store_count
|
||||
FROM distribution_detail_records d
|
||||
WHERE d.business_date >= $1::date AND d.business_date < ($1::date + INTERVAL '1 month')
|
||||
AND NOT d.is_return AND d.item_code IS NOT NULL
|
||||
FROM mv_distribution_monthly d
|
||||
WHERE d.report_month = $1::date
|
||||
GROUP BY d.item_code, d.item_name, d.unit, d.major_category, d.minor_category
|
||||
),
|
||||
inv_items AS (
|
||||
@@ -1757,16 +1760,14 @@ router.get('/distribution/reconciliation', async (req: AuthRequest, res) => {
|
||||
round(sum(d.outbound_total_amount)::numeric,2) AS dist_amt,
|
||||
round(sum(d.cost_excl_tax_amount)::numeric,2) AS dist_cost_excl_tax,
|
||||
count(DISTINCT d.item_code) AS item_count
|
||||
FROM distribution_detail_records d
|
||||
WHERE d.business_date >= $1::date AND d.business_date < ($1::date + INTERVAL '1 month')
|
||||
AND NOT d.is_return AND d.item_code IS NOT NULL
|
||||
FROM mv_distribution_monthly d
|
||||
WHERE d.report_month = $1::date
|
||||
GROUP BY COALESCE(NULLIF(d.minor_category,''),'未分类')
|
||||
),
|
||||
item_cat AS (
|
||||
SELECT DISTINCT item_code, COALESCE(NULLIF(minor_category,''),'未分类') AS minor_category
|
||||
FROM distribution_detail_records
|
||||
WHERE business_date >= $1::date AND business_date < ($1::date + INTERVAL '1 month')
|
||||
AND item_code IS NOT NULL
|
||||
FROM mv_distribution_monthly
|
||||
WHERE report_month = $1::date
|
||||
),
|
||||
inv AS (
|
||||
SELECT ic.minor_category,
|
||||
@@ -2080,6 +2081,14 @@ router.get('/sales-driven/production-plan', async (req: AuthRequest, res) => {
|
||||
await client.query(`REFRESH MATERIALIZED VIEW CONCURRENTLY mv_dish_sales_monthly`)
|
||||
}
|
||||
|
||||
// 检查配送物化视图是否包含当前月份数据
|
||||
const distMvCheck = await client.query(`
|
||||
SELECT 1 FROM mv_distribution_monthly WHERE report_month = $1::date LIMIT 1
|
||||
`, [month])
|
||||
if (distMvCheck.rows.length === 0) {
|
||||
await client.query(`REFRESH MATERIALIZED VIEW CONCURRENTLY mv_distribution_monthly`)
|
||||
}
|
||||
|
||||
// 从物化视图创建临时表(索引扫描,毫秒级)
|
||||
await client.query(`
|
||||
CREATE TEMP TABLE IF NOT EXISTS tmp_sales_agg AS
|
||||
@@ -2159,9 +2168,8 @@ router.get('/sales-driven/production-plan', async (req: AuthRequest, res) => {
|
||||
LEFT JOIN analytics.dim_material m ON b.material_code = m.material_code
|
||||
LEFT JOIN (
|
||||
SELECT d.item_name, round(sum(d.outbound_total_amount)::numeric / NULLIF(sum(d.total_quantity), 0), 4) AS unit_price
|
||||
FROM distribution_detail_records d
|
||||
WHERE d.business_date >= $1::date AND d.business_date < ($1::date + INTERVAL '1 month')
|
||||
AND NOT d.is_return AND d.item_name IS NOT NULL
|
||||
FROM mv_distribution_monthly d
|
||||
WHERE d.report_month = $1::date
|
||||
GROUP BY d.item_name
|
||||
) ic ON m.material_name = ic.item_name
|
||||
GROUP BY s.store_code, b.material_code, m.material_name, b.unit
|
||||
@@ -2279,12 +2287,33 @@ router.get('/sales-driven/production-plan', async (req: AuthRequest, res) => {
|
||||
SELECT d.item_code, d.item_name,
|
||||
round(sum(d.total_quantity)::numeric,4) AS dist_qty,
|
||||
round(sum(d.outbound_total_amount)::numeric,2) AS dist_amt
|
||||
FROM distribution_detail_records d
|
||||
WHERE d.business_date >= $1::date AND d.business_date < ($1::date + INTERVAL '1 month')
|
||||
AND NOT d.is_return AND d.item_code IS NOT NULL
|
||||
FROM mv_distribution_monthly d
|
||||
WHERE d.report_month = $1::date
|
||||
AND d.distribution_center_code = '2'
|
||||
GROUP BY d.item_code, d.item_name
|
||||
),
|
||||
ck_inventory AS (
|
||||
SELECT f.material_code,
|
||||
round(sum(f.consumption_quantity)::numeric,4) AS consumption_qty,
|
||||
round(sum(f.ending_quantity)::numeric,4) AS ending_qty
|
||||
FROM analytics.fact_inventory_snapshot f
|
||||
WHERE f.snapshot_date >= $1::date AND f.snapshot_date < ($1::date + INTERVAL '1 month')
|
||||
AND f.store_code = '2'
|
||||
GROUP BY f.material_code
|
||||
),
|
||||
other_wh_inventory AS (
|
||||
SELECT f.material_code,
|
||||
round(sum(f.consumption_quantity)::numeric,4) AS consumption_qty,
|
||||
round(sum(f.ending_quantity)::numeric,4) AS ending_qty
|
||||
FROM analytics.fact_inventory_snapshot f
|
||||
WHERE f.snapshot_date >= $1::date AND f.snapshot_date < ($1::date + INTERVAL '1 month')
|
||||
AND f.store_code IN (
|
||||
SELECT cost_unit_code FROM inventory_store_mapping
|
||||
WHERE sales_store_code IS NULL OR sales_store_code = ''
|
||||
)
|
||||
AND f.store_code != '2'
|
||||
GROUP BY f.material_code
|
||||
),
|
||||
store_consumption AS (
|
||||
SELECT f.material_code,
|
||||
round(sum(f.consumption_quantity)::numeric,4) AS consumption_qty,
|
||||
@@ -2293,6 +2322,10 @@ router.get('/sales-driven/production-plan', async (req: AuthRequest, res) => {
|
||||
round(sum(f.ending_amount)::numeric,2) AS ending_amt
|
||||
FROM analytics.fact_inventory_snapshot f
|
||||
WHERE f.snapshot_date >= $1::date AND f.snapshot_date < ($1::date + INTERVAL '1 month')
|
||||
AND f.store_code NOT IN (
|
||||
SELECT cost_unit_code FROM inventory_store_mapping
|
||||
WHERE sales_store_code IS NULL OR sales_store_code = ''
|
||||
)
|
||||
GROUP BY f.material_code
|
||||
)
|
||||
SELECT COALESCE(p.product_code, dist.item_code) AS product_code,
|
||||
@@ -2302,6 +2335,10 @@ router.get('/sales-driven/production-plan', async (req: AuthRequest, res) => {
|
||||
COALESCE(p.theoretical_cost, 0) AS theoretical_cost,
|
||||
COALESCE(dist.dist_qty, 0) AS dist_qty,
|
||||
COALESCE(dist.dist_amt, 0) AS dist_amt,
|
||||
COALESCE(ck.consumption_qty, 0) AS ck_consumption_qty,
|
||||
COALESCE(ck.ending_qty, 0) AS ck_ending_qty,
|
||||
COALESCE(owh.consumption_qty, 0) AS other_wh_consumption_qty,
|
||||
COALESCE(owh.ending_qty, 0) AS other_wh_ending_qty,
|
||||
COALESCE(sc.consumption_qty, 0) AS consumption_qty,
|
||||
COALESCE(sc.consumption_amt, 0) AS consumption_amt,
|
||||
COALESCE(sc.ending_qty, 0) AS ending_qty,
|
||||
@@ -2319,6 +2356,8 @@ router.get('/sales-driven/production-plan', async (req: AuthRequest, res) => {
|
||||
ELSE NULL END AS inventory_accumulation_rate
|
||||
FROM ck_production p
|
||||
FULL OUTER JOIN ck_distribution dist ON p.product_code = dist.item_code
|
||||
FULL OUTER JOIN ck_inventory ck ON COALESCE(p.product_code, dist.item_code) = ck.material_code
|
||||
FULL OUTER JOIN other_wh_inventory owh ON COALESCE(p.product_code, dist.item_code) = owh.material_code
|
||||
FULL OUTER JOIN store_consumption sc ON COALESCE(p.product_code, dist.item_code) = sc.material_code
|
||||
ORDER BY COALESCE(p.actual_cost, 0) DESC
|
||||
LIMIT 50
|
||||
@@ -2384,6 +2423,10 @@ router.get('/sales-driven/production-plan', async (req: AuthRequest, res) => {
|
||||
theoretical_cost: parseNum(row.theoretical_cost),
|
||||
dist_qty: parseNum(row.dist_qty),
|
||||
dist_amt: parseNum(row.dist_amt),
|
||||
ck_consumption_qty: parseNum(row.ck_consumption_qty),
|
||||
ck_ending_qty: parseNum(row.ck_ending_qty),
|
||||
other_wh_consumption_qty: parseNum(row.other_wh_consumption_qty),
|
||||
other_wh_ending_qty: parseNum(row.other_wh_ending_qty),
|
||||
consumption_qty: parseNum(row.consumption_qty),
|
||||
consumption_amt: parseNum(row.consumption_amt),
|
||||
ending_qty: parseNum(row.ending_qty),
|
||||
|
||||
Reference in New Issue
Block a user