fix(cost-center): 归档历史人工商品且保留原始数据

This commit is contained in:
yml2213
2026-11-02 15:47:04 +08:00
parent 4ffc1c09be
commit e7b3e46378
4 changed files with 126 additions and 6 deletions
@@ -5,6 +5,7 @@
* aff: 自动发货 / tpl: 接单模板 / wo: 工单证据 / man: 人工。
*/
import {
ClearOutlined,
DeleteOutlined,
DollarOutlined,
EditOutlined,
@@ -41,6 +42,7 @@ import { AdminRangePicker } from '@/components/admin/AdminDatePicker'
import dayjs from '@/lib/dayjs'
import {
applyAdminTemplateProductBootstrap,
archiveAdminHistoricalManualProducts,
createAdminProductCatalog,
deleteAdminProductCatalog,
fetchAdminProductCatalog,
@@ -105,6 +107,7 @@ export function ProductCatalogPanel() {
const [productStatusUpdatingId, setProductStatusUpdatingId] = useState<number | null>(null)
const [bootstrapPreviewing, setBootstrapPreviewing] = useState(false)
const [bootstrapApplying, setBootstrapApplying] = useState(false)
const [archivingHistoricalManuals, setArchivingHistoricalManuals] = useState(false)
const bootstrapSyncOptionsRef = useRef<BootstrapSyncOptions>({ includeDisabledTemplates: false })
const [productPage, setProductPage] = useState(1)
const [productPageSize, setProductPageSize] = useState(ADMIN_DEFAULT_PAGE_SIZE)
@@ -441,6 +444,41 @@ export function ProductCatalogPanel() {
}
}
async function archiveHistoricalManuals() {
setArchivingHistoricalManuals(true)
try {
const preview = (await archiveAdminHistoricalManualProducts()).data
if (preview.candidateCount === 0) {
message.info('没有需要归档的历史人工商品')
return
}
modal.confirm({
title: '归档历史人工商品',
content: `将归档 ${preview.candidateCount} 个仅被已归档订单引用的历史人工商品。不会删除订单、订单项、成本或工单数据,商品只是从当前目录隐藏。`,
okText: '确认归档',
cancelText: '取消',
onOk: async () => {
setArchivingHistoricalManuals(true)
try {
const result = (await archiveAdminHistoricalManualProducts({ execute: true })).data
message.success(`已归档 ${result.archivedCount} 个历史人工商品`)
await queryClient.invalidateQueries({ queryKey: ['admin-cost-center-products'] })
void queryClient.invalidateQueries({ queryKey: ['admin-cost-center-overview'] })
void queryClient.invalidateQueries({ queryKey: ['admin-cost-center-options'] })
} catch (error) {
message.error(error instanceof Error ? error.message : '归档历史人工商品失败')
} finally {
setArchivingHistoricalManuals(false)
}
},
})
} catch (error) {
message.error(error instanceof Error ? error.message : '读取历史人工商品失败')
} finally {
setArchivingHistoricalManuals(false)
}
}
function deleteProduct(item: ProductCatalogItem) {
const affiliateHint =
item.source === 'affiliate_dash'
@@ -659,6 +697,15 @@ export function ProductCatalogPanel() {
extra={
canConfig ? (
<Space size={6} wrap>
{productSourceFilter === 'manual' ? (
<Button
icon={<ClearOutlined />}
loading={archivingHistoricalManuals}
onClick={() => void archiveHistoricalManuals()}
>
归档历史人工商品
</Button>
) : null}
{productSourceFilter === 'work_order' ? (
<>
<AdminRangePicker
@@ -107,6 +107,15 @@ export function createAdminProductCatalog(payload: Record<string, unknown>) {
return apiPost<{ product: ProductCatalogItem }>('/api/v1/admin/cost-center/products', payload)
}
export function archiveAdminHistoricalManualProducts(payload: { execute?: boolean } = {}) {
return apiPost<{
dryRun: boolean
candidateCount: number
archivedCount: number
sample: Array<{ id: number; canonical_sku: string; canonical_name: string }>
}>('/api/v1/admin/cost-center/products/archive-historical-manual', payload)
}
export function syncAdminAffiliateDashProducts() {
return apiPost<{ createdCount: number; updatedCount: number; productCount: number }>(
'/api/v1/admin/cost-center/affiliate-products/sync',