perf: Phase 12 — 物化剩余9个慢函数,全量API优化完成
新增物化视图: - mv_store_action_priority_deep_monthly (10.7s→3ms) - mv_inventory_cost_classified_monthly (37ms→3ms, 消除重复调用) - mv_dish_pair_summary_monthly (171s→3ms) - mv_store_area_efficiency_monthly (10.1s→3ms) - mv_store_site_profile_monthly (9.3s→3ms) - mv_site_segment_benchmark_monthly (12s→3ms) - mv_store_site_replication_monthly (12.6s→3ms) - mv_store_overlap_risk_monthly (88s→3ms) - mv_district_site_benchmark_monthly (56s→3ms) 后端: 所有 fn_ 函数调用替换为物化视图查询 前端: BossPage 添加 MonthPicker 组件并传递 month 参数 修复: cost/category-benchmark 排序列名 store_code→sales_store_code 总计: 21个物化视图, 全部API响应<1.7秒
This commit is contained in:
@@ -8,6 +8,7 @@ import { LoadingSpinner } from '@/components/LoadingSpinner'
|
||||
import { CollapsibleSection } from '@/components/CollapsibleSection'
|
||||
import { formatCurrency, formatNumber, formatPercent } from '@/lib/utils'
|
||||
import { Crown, TrendingDown, AlertTriangle, TrendingUp, Building2, Receipt, Target, ChevronDown } from 'lucide-react'
|
||||
import { MonthPicker } from '@/components/MonthPicker'
|
||||
|
||||
const RISK_COLORS: Record<string, string> = { '红色': '#ef4444', '黄色': '#eab308', '绿色': '#22c55e' }
|
||||
|
||||
@@ -64,46 +65,47 @@ function ProfitOppItem({ index, o, opp, pct, confColor }: { index: number; o: an
|
||||
|
||||
export function BossPage() {
|
||||
const navigate = useNavigate()
|
||||
const [month, setMonth] = useState('2026-04')
|
||||
|
||||
const { data: overview, isLoading: odLoading } = useQuery({
|
||||
queryKey: ['overview'],
|
||||
queryFn: () => api.get('/overview'),
|
||||
queryKey: ['overview', month],
|
||||
queryFn: () => api.get('/overview', { params: { month } }),
|
||||
})
|
||||
const { data: daily, isLoading: dailyLoading } = useQuery({
|
||||
queryKey: ['overview/daily'],
|
||||
queryFn: () => api.get('/overview/daily'),
|
||||
queryKey: ['overview/daily', month],
|
||||
queryFn: () => api.get('/overview/daily', { params: { month } }),
|
||||
})
|
||||
const { data: expenseData, isLoading: exLoading } = useQuery({
|
||||
queryKey: ['store-expense/overview'],
|
||||
queryFn: () => api.get('/store-expense/overview'),
|
||||
queryKey: ['store-expense/overview', month],
|
||||
queryFn: () => api.get('/store-expense/overview', { params: { month } }),
|
||||
})
|
||||
const { data: waterfallData, isLoading: wfLoading } = useQuery({
|
||||
queryKey: ['overview/profit-waterfall'],
|
||||
queryFn: () => api.get('/overview/profit-waterfall'),
|
||||
queryKey: ['overview/profit-waterfall', month],
|
||||
queryFn: () => api.get('/overview/profit-waterfall', { params: { month } }),
|
||||
})
|
||||
const { data: riskData, isLoading: riskLoading } = useQuery({
|
||||
queryKey: ['stores/risk'],
|
||||
queryFn: () => api.get('/stores/risk'),
|
||||
queryKey: ['stores/risk', month],
|
||||
queryFn: () => api.get('/stores/risk', { params: { month } }),
|
||||
})
|
||||
const { data: priorityData, isLoading: priorityLoading } = useQuery({
|
||||
queryKey: ['stores/priority'],
|
||||
queryFn: () => api.get('/stores/priority'),
|
||||
queryKey: ['stores/priority', month],
|
||||
queryFn: () => api.get('/stores/priority', { params: { month } }),
|
||||
})
|
||||
const { data: costOverview, isLoading: costLoading } = useQuery({
|
||||
queryKey: ['cost-analysis/store-overview'],
|
||||
queryFn: () => api.get('/cost-analysis/store-overview'),
|
||||
queryKey: ['cost-analysis/store-overview', month],
|
||||
queryFn: () => api.get('/cost-analysis/store-overview', { params: { month } }),
|
||||
})
|
||||
const { data: expenseStructure, isLoading: structLoading } = useQuery({
|
||||
queryKey: ['store-expense/expense-structure'],
|
||||
queryFn: () => api.get('/store-expense/expense-structure'),
|
||||
queryKey: ['store-expense/expense-structure', month],
|
||||
queryFn: () => api.get('/store-expense/expense-structure', { params: { month } }),
|
||||
})
|
||||
const { data: profitRankingData } = useQuery({
|
||||
queryKey: ['overview/store-profit-ranking'],
|
||||
queryFn: () => api.get('/overview/store-profit-ranking'),
|
||||
queryKey: ['overview/store-profit-ranking', month],
|
||||
queryFn: () => api.get('/overview/store-profit-ranking', { params: { month } }),
|
||||
})
|
||||
const { data: profitOppData } = useQuery({
|
||||
queryKey: ['overview/profit-opportunity'],
|
||||
queryFn: () => api.get('/overview/profit-opportunity'),
|
||||
queryKey: ['overview/profit-opportunity', month],
|
||||
queryFn: () => api.get('/overview/profit-opportunity', { params: { month } }),
|
||||
})
|
||||
|
||||
const pageLoading = odLoading || dailyLoading || exLoading || wfLoading || riskLoading || priorityLoading
|
||||
@@ -203,12 +205,15 @@ export function BossPage() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* 标题 */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Crown className="text-yellow-500" size={24} />
|
||||
<div>
|
||||
<h1 className="text-xl font-bold">老板驾驶舱</h1>
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">经营全景 · 2026年4月</p>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Crown className="text-yellow-500" size={24} />
|
||||
<div>
|
||||
<h1 className="text-xl font-bold">老板驾驶舱</h1>
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">经营全景 · {month}</p>
|
||||
</div>
|
||||
</div>
|
||||
<MonthPicker month={month} onChange={setMonth} />
|
||||
</div>
|
||||
|
||||
{/* ① 核心经营指标 */}
|
||||
|
||||
Reference in New Issue
Block a user