diff --git a/apps/backend/src/routes/admin/cost-center-catalog.ts b/apps/backend/src/routes/admin/cost-center-catalog.ts index d6f6e473..e79e4671 100644 --- a/apps/backend/src/routes/admin/cost-center-catalog.ts +++ b/apps/backend/src/routes/admin/cost-center-catalog.ts @@ -6,6 +6,8 @@ */ import { Router } from 'express' +import { ensureVoucherChannelRules } from '../../services/fulfillment/channel-assign-service.js' + import { backfillAdminNormalizedQuantities, backfillAdminProductMappings, @@ -364,4 +366,20 @@ router.post( ), ) +router.post( + '/cost-center/voucher-products/ensure-rules', + requireAdminPermission('finance.config'), + createJsonHandler(() => ensureVoucherChannelRules(), { + successMessage: '卡券渠道规则已补齐', + errorMessage: '补齐卡券渠道规则失败', + scope: '[admin/cost-center/voucher-products/ensure-rules]', + audit: (_req, data) => ({ + action: 'voucher_channel_rules_ensured', + targetType: 'sku_channel_rules', + targetId: '', + data: data as Record, + }), + }), +) + export default router diff --git a/apps/frontend/src/pages/admin/cost-center/CostExceptionPanel.tsx b/apps/frontend/src/pages/admin/cost-center/CostExceptionPanel.tsx index 44a32559..18c9fffb 100644 --- a/apps/frontend/src/pages/admin/cost-center/CostExceptionPanel.tsx +++ b/apps/frontend/src/pages/admin/cost-center/CostExceptionPanel.tsx @@ -47,8 +47,11 @@ const FULFILLMENT_MODE_TAG_COLORS: Record = { const FULFILLMENT_MODE_LABELS: Record = { work_order: '接单平台', affiliate_dash: '自动发货', - legacy_platform: '历史平台', + third_party_voucher: '第三方卡券', manual: '人工客服', + pending: '待分类', + archived: '历史归档', + unclassified: '未分类', } const REASON_LABELS: Record = { @@ -58,6 +61,7 @@ const REASON_LABELS: Record = { cost_missing: '成本缺失', manual_config_missing: '人工 SKU 未配置', manual_cost_missing: '人工成本未记录', + carrier_missing: '缺少履约载体', } const RANGE_OPTIONS = [ diff --git a/apps/frontend/src/pages/admin/cost-center/VoucherSkuPanel.tsx b/apps/frontend/src/pages/admin/cost-center/VoucherSkuPanel.tsx index 5450071b..d8ab6c86 100644 --- a/apps/frontend/src/pages/admin/cost-center/VoucherSkuPanel.tsx +++ b/apps/frontend/src/pages/admin/cost-center/VoucherSkuPanel.tsx @@ -4,13 +4,28 @@ * 商品编码固定为 `tpv:`;成本写入 `sys_third_party_voucher_cost` 的 * 商品级版本,由成本快照管道消费。 */ -import { ReloadOutlined } from '@ant-design/icons' -import { Button, Card, Form, Input, InputNumber, Modal, Tag, Typography, message } from 'antd' +import { ReloadOutlined, SyncOutlined } from '@ant-design/icons' +import { + Button, + Card, + Form, + Input, + InputNumber, + Modal, + Space, + Tag, + Typography, + message, +} from 'antd' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useState } from 'react' import PercentTable from '@/components/common/PercentTable' -import { fetchAdminProductCatalog, setAdminProductPrice } from '@/services/admin' +import { + ensureVoucherChannelRules, + fetchAdminProductCatalog, + setAdminProductPrice, +} from '@/services/admin' import type { ProductCatalogItem } from '@/types/admin' import { useCostCenter } from './CostCenterContext' @@ -21,6 +36,7 @@ export default function VoucherSkuPanel() { const { canConfig, refreshAll } = useCostCenter() const [page] = useState(1) const [selected, setSelected] = useState(null) + const [ensuringRules, setEnsuringRules] = useState(false) const [form] = Form.useForm<{ amountFen: number; changeReason?: string }>() const query = useQuery({ @@ -49,6 +65,20 @@ export default function VoucherSkuPanel() { onError: (error) => message.error(error instanceof Error ? error.message : '保存失败'), }) + async function handleEnsureRules() { + setEnsuringRules(true) + try { + const result = await ensureVoucherChannelRules() + message.success( + `卡券商品 ${result.data.productCount} 个,已补齐渠道规则 ${result.data.created} 条`, + ) + } catch (error) { + message.error(error instanceof Error ? error.message : '补齐卡券渠道规则失败') + } finally { + setEnsuringRules(false) + } + } + const items = query.data?.data.items || [] const total = query.data?.data.pagination.total || 0 @@ -99,9 +129,19 @@ export default function VoucherSkuPanel() { } onClick={() => void query.refetch()}> - 刷新 - + + + + } > diff --git a/apps/frontend/src/services/admin/cost-center.ts b/apps/frontend/src/services/admin/cost-center.ts index c00dcc23..96abe88a 100644 --- a/apps/frontend/src/services/admin/cost-center.ts +++ b/apps/frontend/src/services/admin/cost-center.ts @@ -519,3 +519,9 @@ export function syncAdminProductPrice(productId: number) { message: string }>(`/api/v1/admin/cost-center/products/${productId}/sync-price`, {}) } + +export function ensureVoucherChannelRules() { + return apiPost<{ created: number; productCount: number }>( + '/api/v1/admin/cost-center/voucher-products/ensure-rules', + ) +}