refactor(finance)!: 事实维度与接口字段统一改名 channel

- migration 114 将 sales_daily_facts.fulfillment_mode 重命名为 channel
- DTO/查询参数/前端类型从 fulfillmentMode 统一为 channel
- 成本中心类型 FulfillmentMode 更名为 FulfillmentChannel,并补齐卡券/待分类/归档取值
- 销售事实响应分组改为 byChannel
This commit is contained in:
yml2213
2026-10-31 20:26:15 +08:00
parent 657e49f324
commit 6cd6401eca
38 changed files with 220 additions and 208 deletions
@@ -271,10 +271,10 @@ export default function AdminOrderDetailPage() {
detail.order.payStatus === 'refunded' || detail.order.orderStatus === 'cancelled'
const canPauseOrClose = !isOrderFulfilled && !isOrderRefundedOrCancelled
const isAutoClaimOrder = Boolean(
detail.order.fulfillmentMode === 'affiliate_dash' ||
(detail.order.fulfillmentMode === 'mixed' && detail.order.canClaimH5),
detail.order.channel === 'affiliate_dash' ||
(detail.order.channel === 'mixed' && detail.order.canClaimH5),
)
const fulfillmentMode = resolveOrderFulfillmentMode(detail.order)
const channel = resolveOrderFulfillmentMode(detail.order)
const voucherLabel = resolveVoucherStateLabel(detail.order.voucherState)
return (
@@ -358,8 +358,8 @@ export default function AdminOrderDetailPage() {
</Descriptions.Item>
<Descriptions.Item label="平台订单号">{detail.order.platformOrderId}</Descriptions.Item>
<Descriptions.Item label="履约模式">
<Tag color={fulfillmentMode.color} style={{ margin: 0 }}>
{fulfillmentMode.label}
<Tag color={channel.color} style={{ margin: 0 }}>
{channel.label}
</Tag>
</Descriptions.Item>
<Descriptions.Item label="核销码">
@@ -55,7 +55,7 @@ import { formatAdminDateTime } from '@/utils/admin-time'
type OrderFilterForm = {
keyword?: string
provider?: string
fulfillmentMode?: string
channel?: string
payStatus?: string
taskStatus?: string
dateRange?: unknown
@@ -74,7 +74,7 @@ export default function AdminOrdersPage() {
const filters = {
keyword: searchParams.get('keyword') || '',
provider: searchParams.get('provider') || '',
fulfillmentMode: searchParams.get('fulfillmentMode') || '',
channel: searchParams.get('channel') || '',
payStatus: searchParams.get('payStatus') || '',
taskStatus: searchParams.get('taskStatus') || '',
dateFrom: searchParams.get('dateFrom') || '',
@@ -86,7 +86,7 @@ export default function AdminOrdersPage() {
[
filters.dateFrom,
filters.dateTo,
filters.fulfillmentMode,
filters.channel,
filters.keyword,
filters.payStatus,
filters.provider,
@@ -106,7 +106,7 @@ export default function AdminOrdersPage() {
form.setFieldsValue({
keyword: filters.keyword || undefined,
provider: filters.provider || undefined,
fulfillmentMode: filters.fulfillmentMode || undefined,
channel: filters.channel || undefined,
payStatus: filters.payStatus || undefined,
taskStatus: filters.taskStatus || undefined,
dateRange:
@@ -117,7 +117,7 @@ export default function AdminOrdersPage() {
}, [
filters.dateFrom,
filters.dateTo,
filters.fulfillmentMode,
filters.channel,
filters.keyword,
filters.payStatus,
filters.provider,
@@ -277,14 +277,14 @@ export default function AdminOrdersPage() {
title: '交付 / 状态',
minWidth: 230,
render: (_, row) => {
const fulfillmentMode = resolveOrderFulfillmentMode(row)
const channel = resolveOrderFulfillmentMode(row)
const voucherLabel = resolveVoucherStateLabel(row.voucherState)
return (
<div className="cell-stack" style={{ gap: 4 }}>
<Space size={[0, 4]} wrap>
<Tag color={fulfillmentMode.color} style={{ margin: 0, fontSize: 11 }}>
{fulfillmentMode.label}
<Tag color={channel.color} style={{ margin: 0, fontSize: 11 }}>
{channel.label}
</Tag>
{row.hasVoucher && voucherLabel ? (
<Tag color="gold" style={{ margin: 0, fontSize: 11 }}>
@@ -413,7 +413,7 @@ export default function AdminOrdersPage() {
form.setFieldsValue({
keyword: undefined,
provider: undefined,
fulfillmentMode: undefined,
channel: undefined,
payStatus: undefined,
taskStatus: undefined,
dateRange: undefined,
@@ -474,7 +474,7 @@ export default function AdminOrdersPage() {
}}
/>
</Form.Item>
<Form.Item name="fulfillmentMode">
<Form.Item name="channel">
<Select
allowClear
placeholder="履约模式"
@@ -32,7 +32,7 @@ import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admi
import { hasAdminPermission } from '@/utils/admin-auth'
import { formatMoney } from './panels/shared'
type FilterForm = { dateRange?: unknown; provider?: string; fulfillmentMode?: string }
type FilterForm = { dateRange?: unknown; provider?: string; channel?: string }
const FULFILLMENT_MODE_OPTIONS = [
{ value: '', label: '全部履约方式' },
@@ -71,8 +71,8 @@ export default function AdminSalesProfitPage() {
dateFrom: string
dateTo: string
provider: string
fulfillmentMode: string
}>({ dateFrom: defaultDateFrom, dateTo: defaultDateTo, provider: '', fulfillmentMode: '' })
channel: string
}>({ dateFrom: defaultDateFrom, dateTo: defaultDateTo, provider: '', channel: '' })
const [page, setPage] = useState(1)
const [pageSize, setPageSize] = useState(ADMIN_DEFAULT_PAGE_SIZE)
const [rebuilding, setRebuilding] = useState(false)
@@ -96,7 +96,7 @@ export default function AdminSalesProfitPage() {
dateFrom: range[0] ? dayjs(range[0]).format('YYYY-MM-DD') : '',
dateTo: range[1] ? dayjs(range[1]).format('YYYY-MM-DD') : '',
provider: values.provider || '',
fulfillmentMode: values.fulfillmentMode || '',
channel: values.channel || '',
})
}
@@ -107,7 +107,7 @@ export default function AdminSalesProfitPage() {
dateFrom: defaultDateFrom,
dateTo: defaultDateTo,
provider: '',
fulfillmentMode: '',
channel: '',
})
}
@@ -180,12 +180,10 @@ export default function AdminSalesProfitPage() {
},
]
const fulfillmentModeColumns: TableColumnsType<
NonNullable<typeof overview>['byFulfillmentMode'][number]
> = [
const channelColumns: TableColumnsType<NonNullable<typeof overview>['byChannel'][number]> = [
{
title: '履约方式',
dataIndex: 'fulfillmentMode',
dataIndex: 'channel',
render: (value: string) => FULFILLMENT_MODE_LABEL[value] || value,
},
{ title: '订单项', dataIndex: 'itemCount', width: 90 },
@@ -223,7 +221,7 @@ export default function AdminSalesProfitPage() {
},
{
title: '履约方式',
dataIndex: 'fulfillmentMode',
dataIndex: 'channel',
width: 100,
render: (value: string) => FULFILLMENT_MODE_LABEL[value] || value,
},
@@ -263,7 +261,7 @@ export default function AdminSalesProfitPage() {
initialValues={{
dateRange: [dayjs().subtract(29, 'day'), dayjs()],
provider: '',
fulfillmentMode: '',
channel: '',
}}
>
<Form.Item name="dateRange" label="销售日期">
@@ -280,7 +278,7 @@ export default function AdminSalesProfitPage() {
]}
/>
</Form.Item>
<Form.Item name="fulfillmentMode" label="履约方式">
<Form.Item name="channel" label="履约方式">
<Select style={{ width: 150 }} options={FULFILLMENT_MODE_OPTIONS} />
</Form.Item>
<Form.Item>
@@ -425,10 +423,10 @@ export default function AdminSalesProfitPage() {
label: '履约方式',
children: (
<PercentTable
rowKey="fulfillmentMode"
rowKey="channel"
size="small"
columns={fulfillmentModeColumns}
dataSource={overview?.byFulfillmentMode || []}
columns={channelColumns}
dataSource={overview?.byChannel || []}
loading={overviewQuery.isLoading}
pagination={false}
scroll={{ x: 800 }}
@@ -71,7 +71,7 @@ export function ChannelMappingPanel() {
queryFn: () =>
fetchAdminChannelMappingDiagnostics({
carrierType: productSourceFilter,
fulfillmentMode: productSourceFilter,
channel: productSourceFilter,
}),
})
const diagnostics = diagnosticsQuery.data?.data
@@ -107,7 +107,7 @@ export function ChannelMappingPanel() {
],
queryFn: () =>
fetchAdminUnmappedMappingGroups({
fulfillmentMode: productSourceFilter,
channel: productSourceFilter,
provider: queueProvider || undefined,
keyword: queueKeyword || undefined,
page: queuePage,
@@ -62,7 +62,7 @@ export default function ChannelMappingQueue({
onCreateMapping,
}: Props) {
const isEmpty = !loading && groupTotal === 0
const hasManual = groups.some((row) => row.fulfillmentMode === 'manual')
const hasManual = groups.some((row) => row.channel === 'manual')
const columns: TableColumnsType<UnmappedMappingGroup> = [
{
@@ -97,7 +97,7 @@ export default function ChannelMappingQueue({
},
{
title: '履约方式',
dataIndex: 'fulfillmentMode',
dataIndex: 'channel',
width: 120,
render: (value: string) => (
<Tag color={FULFILLMENT_MODE_COLORS[value] || 'default'}>
@@ -199,7 +199,7 @@ export default function ChannelMappingQueue({
<PercentTable<UnmappedMappingGroup>
tableLayout="fixed"
rowKey={(row) => `${row.provider}-${row.shopId}-${row.fulfillmentMode}-${row.skuCode}`}
rowKey={(row) => `${row.provider}-${row.shopId}-${row.channel}-${row.skuCode}`}
size="small"
columns={columns}
dataSource={groups}
@@ -20,7 +20,7 @@ export type CostCenterContextValue = {
/** 商品/映射按 canonical_sku 前缀筛选。 */
productSourceFilter?: ProductSource
/** 成本项/版本/异常按履约方式筛选。 */
fulfillmentModeFilter?: 'affiliate_dash' | 'work_order' | 'third_party_voucher' | 'manual'
channelFilter?: 'affiliate_dash' | 'work_order' | 'third_party_voucher' | 'manual'
options?: {
providers: string[]
@@ -108,7 +108,7 @@ export default function CostCenterShell() {
const scopeLabel = SCOPES.find((item) => item.key === scope)?.label ?? '全部'
const scopeHint = SCOPES.find((item) => item.key === scope)?.hint ?? ''
const productSourceFilter = scope === 'all' ? undefined : scope
const fulfillmentModeFilter =
const channelFilter =
scope === 'all'
? undefined
: (scope as 'affiliate_dash' | 'work_order' | 'third_party_voucher' | 'manual')
@@ -122,7 +122,7 @@ export default function CostCenterShell() {
setScope,
scopeLabel,
productSourceFilter,
fulfillmentModeFilter,
channelFilter,
options,
refreshAll,
}}
@@ -30,7 +30,7 @@ import {
fetchAdminCostExceptions,
resolveAdminCostException,
} from '@/services/admin'
import type { CostException, FulfillmentMode } from '@/types/admin'
import type { CostException, FulfillmentChannel } from '@/types/admin'
import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admin-pagination'
import { formatAdminDateTime } from '@/utils/admin-time'
@@ -69,7 +69,7 @@ const RANGE_OPTIONS = [
export function CostExceptionPanel() {
const { message } = App.useApp()
const { canConfig, fulfillmentModeFilter, refreshAll } = useCostCenter()
const { canConfig, channelFilter, refreshAll } = useCostCenter()
const [status, setStatus] = useState('open')
const [rangeDays, setRangeDays] = useState(1)
@@ -88,12 +88,12 @@ export function CostExceptionPanel() {
const summaryQuery = useQuery({
queryKey: [
'admin-cost-center-exceptions-summary',
fulfillmentModeFilter,
channelFilter,
rangeDays <= 0 ? 1 : rangeDays,
],
queryFn: () =>
fetchAdminCostExceptionSummary({
fulfillmentMode: fulfillmentModeFilter,
channel: channelFilter,
recentDays: rangeDays <= 0 ? 1 : rangeDays,
}),
})
@@ -102,7 +102,7 @@ export function CostExceptionPanel() {
const exceptionsQuery = useQuery({
queryKey: [
'admin-cost-center-exceptions',
fulfillmentModeFilter,
channelFilter,
status,
rangeDays,
reason,
@@ -112,7 +112,7 @@ export function CostExceptionPanel() {
queryFn: () =>
fetchAdminCostExceptions({
status: status || undefined,
fulfillmentMode: fulfillmentModeFilter,
channel: channelFilter,
reason,
since:
rangeDays > 0
@@ -177,9 +177,9 @@ export function CostExceptionPanel() {
},
{
title: '履约方式',
dataIndex: 'fulfillmentMode',
dataIndex: 'channel',
width: 120,
render: (value: FulfillmentMode | '') =>
render: (value: FulfillmentChannel | '') =>
value ? (
<Tag color={FULFILLMENT_MODE_TAG_COLORS[value] || 'default'}>
{FULFILLMENT_MODE_LABELS[value] || value}
@@ -121,8 +121,7 @@ export function CostPreviewModal({ open, orderItemId, onClose }: CostPreviewModa
{preview.context.skuCode} · {preview.context.skuName || '-'}
</Descriptions.Item>
<Descriptions.Item label="履约方式">
{FULFILLMENT_MODE_LABELS[preview.context.fulfillmentMode] ||
preview.context.fulfillmentMode}
{FULFILLMENT_MODE_LABELS[preview.context.channel] || preview.context.channel}
</Descriptions.Item>
<Descriptions.Item label="标准商品">
{preview.product
@@ -37,7 +37,7 @@ export function WorkOrderPricePanel() {
fetchAdminCostVersions({
keyword: keyword || undefined,
status: status || undefined,
fulfillmentMode: 'work_order',
channel: 'work_order',
page,
pageSize,
}),
@@ -2,7 +2,7 @@
* 成本中心前端共享层:类型、常量、标签与页面上下文。
*
* 口径(2026-10-23 重写):
* 1. 履约方式唯一真源是 order_items.fulfillment_mode,前端只读不改;
* 1. 履约渠道唯一真源是 order_items.fulfillment_channel + status,前端只读不改;
* 2. 商品来源由 canonical_sku 前缀推导,前端不再存「商品载体」字段;
* 3. 成本项用 cost_model 表达「成本怎么算」,没有「适用载体」这一列。
*/
+1 -1
View File
@@ -32,7 +32,7 @@ export type AdminSalesProfitParams = {
dateFrom?: string
dateTo?: string
provider?: string
fulfillmentMode?: string
channel?: string
page?: number
pageSize?: number
}
+5 -5
View File
@@ -2,7 +2,7 @@
* 履约渠道:v2 唯一真源是 order_items.fulfillment_channel;
* legacy_platform 仅历史数据兜底显示。
*/
export type FulfillmentMode =
export type FulfillmentChannel =
| 'work_order'
| 'affiliate_dash'
| 'third_party_voucher'
@@ -143,7 +143,7 @@ export interface CostException {
subjectId: number
orderItemId: number | null
workOrderId: number | null
fulfillmentMode: FulfillmentMode | ''
channel: FulfillmentChannel | ''
reason: string
status: 'open' | 'resolved' | 'ignored'
detail: Record<string, unknown>
@@ -265,7 +265,7 @@ export interface CostPreview {
shopId: string
skuCode: string
skuName: string
fulfillmentMode: FulfillmentMode
channel: FulfillmentChannel
quantity: number
normalizedQuantity: number
revenueFen: number
@@ -343,7 +343,7 @@ export interface CostExceptionListResult {
}
export interface ChannelMappingDiagnostics {
fulfillmentMode: string | null
channel: string | null
productTotal: number
mappingTotal: number
mappedProductTotal: number
@@ -357,7 +357,7 @@ export interface UnmappedMappingGroup {
shopId: string
skuCode: string
skuName: string
fulfillmentMode: string
channel: string
itemCount: number
lastSeenAt: string | null
}
+3 -3
View File
@@ -93,8 +93,8 @@ export type AdminSalesProfitOverview = {
costMissingCount: number
grossProfitFen: number
}>
byFulfillmentMode: Array<{
fulfillmentMode: string
byChannel: Array<{
channel: string
itemCount: number
grossRevenueFen: number
costAmountFen: number
@@ -121,7 +121,7 @@ export type AdminSalesDailyFact = {
canonicalProductId: number | null
canonicalSku: string
canonicalName: string
fulfillmentMode: string
channel: string
orderCount: number
itemCount: number
quantity: number
+4 -4
View File
@@ -26,7 +26,7 @@ export interface AdminOrderItemSummary {
skuNick: string
matchSku: string
quantity: number
fulfillmentMode: AdminOrderItemFulfillmentMode
channel: AdminOrderItemFulfillmentMode
}
export interface AdminOrderListItem {
@@ -62,7 +62,7 @@ export interface AdminOrderListItem {
claimStatus?: string
claimStatusReason?: string
claimStatusUpdatedAt?: string | null
fulfillmentMode: AdminOrderFulfillmentMode
channel: AdminOrderFulfillmentMode
manualItemCount: number
pendingItemCount: number
/** 可确认履约的人工项数量 */
@@ -88,7 +88,7 @@ export interface AdminOrderDetail {
claimStatus?: string
claimStatusReason?: string
claimStatusUpdatedAt?: string | null
fulfillmentMode: AdminOrderFulfillmentMode
channel: AdminOrderFulfillmentMode
manualItemCount: number
pendingItemCount: number
/** 可确认履约的人工项数量 */
@@ -128,7 +128,7 @@ export interface AdminOrderDetail {
refundedFen: number
/** 还可退金额(分) */
refundableFen: number
fulfillmentMode: AdminOrderItemFulfillmentMode
channel: AdminOrderItemFulfillmentMode
deliveryMode: string
spec: Record<string, unknown>
}>
+2 -2
View File
@@ -219,7 +219,7 @@ export function resolveOrderPlatformBadge(row: { platform?: string; provider?: s
}
export function resolveOrderFulfillmentMode(row: {
fulfillmentMode?:
channel?:
| 'work_order'
| 'affiliate_dash'
| 'third_party_voucher'
@@ -229,7 +229,7 @@ export function resolveOrderFulfillmentMode(row: {
| 'archived'
| 'mixed'
}): { label: string; color: string } {
switch (row.fulfillmentMode) {
switch (row.channel) {
case 'affiliate_dash':
return { label: '⚡ 自动发货', color: 'blue' }
case 'work_order':
+1 -1
View File
@@ -28,7 +28,7 @@ export const adminOrderExecutorOptions = [
{ label: '人工履约', value: 'manual_dispatch' },
]
/** 订单列表按 order_items.fulfillment_mode 聚合后的履约方式筛选。 */
/** 订单列表按 order_items.fulfillment_channel 聚合后的渠道筛选。 */
export const adminOrderFulfillmentModeOptions = [
{ label: '自动发货', value: 'affiliate_dash' },
{ label: '接单平台', value: 'work_order' },