perf(cost-center): 优化总览刷新查询与加载占位
This commit is contained in:
@@ -89,12 +89,11 @@ export async function getAdminCostCenterOverview(params: { scope?: string } = {}
|
||||
(
|
||||
SELECT COUNT(*)::int
|
||||
FROM cost_exceptions ce
|
||||
JOIN order_items oi ON oi.id = ce.order_item_id
|
||||
WHERE ce.status = 'open'
|
||||
AND (
|
||||
$1::text IS NULL
|
||||
OR ce.order_item_id IN (
|
||||
SELECT oi.id FROM order_items oi WHERE COALESCE(oi.fulfillment_channel, CASE WHEN oi.fulfillment_status = 'archived' THEN 'archived' ELSE 'pending' END) = $1
|
||||
)
|
||||
OR COALESCE(oi.fulfillment_channel, CASE WHEN oi.fulfillment_status = 'archived' THEN 'archived' ELSE 'pending' END) = $1
|
||||
)
|
||||
) AS open_exception_count,
|
||||
(
|
||||
|
||||
@@ -298,6 +298,16 @@ test('人工客服筛选排除已有其他主渠道的旧 man 商品', () => {
|
||||
assert.ok(sql.includes('BTRIM(live_channel.internal_sku) = BTRIM(p.canonical_name)'))
|
||||
})
|
||||
|
||||
test('成本中心总览异常统计必须走异常表关联,不能全表扫描订单项子查询', async () => {
|
||||
const { readFile } = await import('node:fs/promises')
|
||||
const source = await readFile(
|
||||
new URL('./cost-center-overview-service.ts', import.meta.url),
|
||||
'utf8',
|
||||
)
|
||||
assert.ok(source.includes('JOIN order_items oi ON oi.id = ce.order_item_id'))
|
||||
assert.ok(!source.includes('ce.order_item_id IN ('))
|
||||
})
|
||||
|
||||
test('tpv: 商品来源归为第三方卡券', async () => {
|
||||
const { productSourceOf } = await import('./cost-center-mappers.js')
|
||||
const assert = (await import('node:assert/strict')).default
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
Tag,
|
||||
Typography,
|
||||
} from 'antd'
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { keepPreviousData, useIsFetching, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import FinanceSubNav from '@/components/admin/FinanceSubNav'
|
||||
@@ -97,6 +97,7 @@ export default function CostCenterShell() {
|
||||
const overviewQuery = useQuery({
|
||||
queryKey: ['admin-cost-center-overview', scope],
|
||||
queryFn: () => fetchAdminCostCenterOverview({ scope }),
|
||||
placeholderData: keepPreviousData,
|
||||
})
|
||||
const optionsQuery = useQuery({
|
||||
queryKey: ['admin-cost-center-options'],
|
||||
@@ -105,6 +106,9 @@ export default function CostCenterShell() {
|
||||
|
||||
const overview = overviewQuery.data?.data
|
||||
const options = optionsQuery.data?.data
|
||||
const costCenterFetching = useIsFetching({
|
||||
predicate: (query) => String(query.queryKey[0] ?? '').startsWith('admin-cost-center'),
|
||||
})
|
||||
|
||||
function refreshAll() {
|
||||
// 按前缀失效:新增一个 admin-cost-center-* 查询不需要再回来改这里。
|
||||
@@ -145,7 +149,7 @@ export default function CostCenterShell() {
|
||||
size="small"
|
||||
icon={<ReloadOutlined />}
|
||||
onClick={refreshAll}
|
||||
loading={overviewQuery.isFetching}
|
||||
loading={costCenterFetching > 0}
|
||||
>
|
||||
刷新
|
||||
</Button>
|
||||
@@ -162,7 +166,7 @@ export default function CostCenterShell() {
|
||||
].map(([label, value]) => (
|
||||
<Col xs={12} md={8} xl={4} key={label}>
|
||||
<Card>
|
||||
<Statistic title={label} value={value as number} />
|
||||
<Statistic title={label} value={overview ? (value as number) : '—'} />
|
||||
</Card>
|
||||
</Col>
|
||||
))}
|
||||
@@ -179,11 +183,13 @@ export default function CostCenterShell() {
|
||||
<Space size={4}>
|
||||
<span>{item.label}</span>
|
||||
<Tag>
|
||||
{Number(
|
||||
item.key === 'all'
|
||||
? (overview?.productTotalCount ?? overview?.productCount ?? 0)
|
||||
: (overview?.productSourceCounts?.[item.key] ?? 0),
|
||||
)}
|
||||
{overview
|
||||
? Number(
|
||||
item.key === 'all'
|
||||
? (overview.productTotalCount ?? overview.productCount ?? 0)
|
||||
: (overview.productSourceCounts?.[item.key] ?? 0),
|
||||
)
|
||||
: '—'}
|
||||
</Tag>
|
||||
</Space>
|
||||
),
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
} from 'antd'
|
||||
import type { TableColumnsType } from 'antd'
|
||||
import PercentTable from '@/components/common/PercentTable'
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { keepPreviousData, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import type { Dayjs } from 'dayjs'
|
||||
|
||||
@@ -152,6 +152,7 @@ export function ProductCatalogPanel() {
|
||||
page: productPage,
|
||||
pageSize: productPageSize,
|
||||
}),
|
||||
placeholderData: keepPreviousData,
|
||||
})
|
||||
|
||||
const products = productsQuery.data?.data.items || []
|
||||
@@ -901,7 +902,7 @@ export function ProductCatalogPanel() {
|
||||
size="small"
|
||||
columns={productColumns}
|
||||
dataSource={products}
|
||||
loading={productsQuery.isLoading}
|
||||
loading={productsQuery.isFetching}
|
||||
rowClassName={(row) => (row.mappingCount > 0 ? 'product-rule-row-mapped' : '')}
|
||||
scroll={{ x: 1180 }}
|
||||
pagination={buildAdminTablePagination({
|
||||
|
||||
Reference in New Issue
Block a user