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
@@ -71,7 +71,7 @@ test('mapAdminOrderListItem formats itemTitle, skuNick, and summary correctly fo
skuNick: '安卓血宴2钻',
matchSku: '安卓血宴2钻',
quantity: 1,
fulfillmentMode: 'pending',
channel: 'pending',
},
])
})
@@ -296,12 +296,11 @@ const CHANNEL_VALUES = new Set(['work_order', 'affiliate_dash', 'third_party_vou
function buildOrderItemForFulfillmentTest(
id: number,
fulfillmentMode: string | null,
value: string | null,
patch: Partial<OrderItemRow> = {},
): OrderItemRow {
const channel = fulfillmentMode && CHANNEL_VALUES.has(fulfillmentMode) ? fulfillmentMode : null
const status =
fulfillmentMode === 'legacy_platform' ? 'archived' : channel ? 'assigned' : 'pending'
const channel = value && CHANNEL_VALUES.has(value) ? value : null
const status = value === 'legacy_platform' ? 'archived' : channel ? 'assigned' : 'pending'
return {
id,
order_id: 501,
@@ -322,7 +321,7 @@ test('人工单即使有已核销快照也仍然判为 manual,核销码单独
vouchers,
})
assert.equal(mapped.fulfillmentMode, 'manual')
assert.equal(mapped.channel, 'manual')
assert.equal(mapped.manualItemCount, 1)
assert.equal(mapped.hasVoucher, true)
assert.equal(mapped.voucherState, 'consumed')
@@ -337,9 +336,9 @@ test('自动发货与历史归档按渠道/状态区分,不再按 provider 猜
items: [buildOrderItemForFulfillmentTest(2, 'legacy_platform')],
})
assert.equal(affiliate.fulfillmentMode, 'affiliate_dash')
assert.equal(affiliate.channel, 'affiliate_dash')
assert.equal(affiliate.canClaimH5, true)
assert.equal(legacy.fulfillmentMode, 'archived')
assert.equal(legacy.channel, 'archived')
assert.equal(legacy.canClaimH5, false)
})
@@ -351,7 +350,7 @@ test('多商品订单出现不同履约方式时显示 mixed,并保留人工
],
})
assert.equal(summary.fulfillmentMode, 'mixed')
assert.equal(summary.channel, 'mixed')
assert.equal(summary.manualItemCount, 1)
assert.equal(summary.pendingItemCount, 0)
})
@@ -367,14 +366,14 @@ test('核销码状态汇总:无、单一、混合', () => {
)
})
test('v2 读取口径:归档状态优先于旧 fulfillment_mode', () => {
test('v2 读取口径:归档状态优先于旧 channel', () => {
const summary = resolveOrderFulfillmentSummary({
items: [buildOrderItemForFulfillmentTest(1, 'work_order', { fulfillment_status: 'archived' })],
})
assert.equal(summary.fulfillmentMode, 'archived')
assert.equal(summary.channel, 'archived')
})
test('v2 读取口径:渠道列优先于旧 fulfillment_mode', () => {
test('v2 读取口径:渠道列优先于旧 channel', () => {
const summary = resolveOrderFulfillmentSummary({
items: [
buildOrderItemForFulfillmentTest(1, 'manual', {
@@ -383,7 +382,7 @@ test('v2 读取口径:渠道列优先于旧 fulfillment_mode', () => {
}),
],
})
assert.equal(summary.fulfillmentMode, 'third_party_voucher')
assert.equal(summary.channel, 'third_party_voucher')
})
test('v2 读取口径:状态 pending 且无渠道时判为待分类', () => {
@@ -395,6 +394,6 @@ test('v2 读取口径:状态 pending 且无渠道时判为待分类', () => {
}),
],
})
assert.equal(summary.fulfillmentMode, 'pending')
assert.equal(summary.channel, 'pending')
assert.equal(summary.pendingItemCount, 1)
})
@@ -56,7 +56,7 @@ export function mapAdminOrderListItem(
skuNick: itSkuNick,
matchSku: itMatchSku,
quantity: Math.max(1, Number(orderItem.quantity || 1)),
fulfillmentMode: resolveOrderItemFulfillmentMode(orderItem),
channel: resolveOrderItemFulfillmentMode(orderItem),
}
})
resolvedItemTitle = itemsList[0]?.itemTitle || ''
@@ -73,7 +73,7 @@ export function mapAdminOrderListItem(
skuNick: resolvedSkuNick,
matchSku: resolvedMatchSku,
quantity: 1,
fulfillmentMode: 'pending',
channel: 'pending',
},
]
}
@@ -120,7 +120,7 @@ export function mapAdminOrderListItem(
items: itemsList,
taskCount: tasks.length,
workOrderCount,
fulfillmentMode: fulfillment.fulfillmentMode,
channel: fulfillment.channel,
manualItemCount: fulfillment.manualItemCount,
pendingItemCount: fulfillment.pendingItemCount,
manualPendingCount: manualConfig.pending,
@@ -138,7 +138,7 @@ export function mapAdminOrderListItem(
}
}
const ORDER_ITEM_FULFILLMENT_MODES = new Set<AdminOrderItemFulfillmentMode>([
const ORDER_ITEM_FULFILLMENT_CHANNELS = new Set<AdminOrderItemFulfillmentMode>([
'work_order',
'affiliate_dash',
'third_party_voucher',
@@ -152,11 +152,11 @@ export function normalizeOrderItemFulfillmentMode(value: unknown): AdminOrderIte
const mode = String(value || '')
.trim()
.toLowerCase() as AdminOrderItemFulfillmentMode
return ORDER_ITEM_FULFILLMENT_MODES.has(mode) ? mode : 'pending'
return ORDER_ITEM_FULFILLMENT_CHANNELS.has(mode) ? mode : 'pending'
}
/**
* v2 读取口径:只认渠道/状态三列,不再回退旧 fulfillment_mode。
* v2 读取口径:只认渠道/状态三列,不再回退旧 channel。
*
* archived → archived
* channel → 该渠道
@@ -215,7 +215,7 @@ export function resolveOrderFulfillmentSummary({
workOrderCount?: number
vouchers?: KuaishouIndustryVoucherRow[]
}): {
fulfillmentMode: AdminOrderFulfillmentMode
channel: AdminOrderFulfillmentMode
manualItemCount: number
pendingItemCount: number
affiliateItemCount: number
@@ -231,36 +231,36 @@ export function resolveOrderFulfillmentSummary({
),
)
let fulfillmentMode: AdminOrderFulfillmentMode
let channel: AdminOrderFulfillmentMode
if (knownModes.length === 1) {
fulfillmentMode = knownModes[0]!
channel = knownModes[0]!
} else if (knownModes.length > 1) {
fulfillmentMode = 'mixed'
channel = 'mixed'
} else if (normalizedItems.length > 0) {
fulfillmentMode = 'pending'
channel = 'pending'
} else if (workOrderCount > 0) {
// 极少数历史订单没有 order_items,只能用载体表兜底;这不是按 provider 猜。
fulfillmentMode = 'work_order'
channel = 'work_order'
} else if (tasks.some((task) => task.executor_key === 'affiliate_dash')) {
fulfillmentMode = 'affiliate_dash'
channel = 'affiliate_dash'
} else if (
tasks.some(
(task) =>
task.executor_key === 'kuaishou_ct_assisted' || task.executor_key === 'kuaishou_feifei',
)
) {
fulfillmentMode = 'archived'
channel = 'archived'
} else {
fulfillmentMode = 'pending'
channel = 'pending'
}
const affiliateItemCount = itemModes.filter((mode) => mode === 'affiliate_dash').length
const voucher = resolveVoucherSummary(vouchers)
const canClaimH5 =
affiliateItemCount > 0 || (normalizedItems.length === 0 && fulfillmentMode === 'affiliate_dash')
affiliateItemCount > 0 || (normalizedItems.length === 0 && channel === 'affiliate_dash')
return {
fulfillmentMode,
channel,
manualItemCount: itemModes.filter((mode) => mode === 'manual').length,
pendingItemCount: itemModes.filter((mode) => mode === 'pending').length,
affiliateItemCount,
@@ -106,7 +106,7 @@ export async function getAdminOrders(
keyword: String(query.keyword || '').trim(),
provider: String(query.provider || '').trim(),
executorKey: String(query.executorKey || '').trim(),
fulfillmentMode: normalizeOrderFulfillmentModeFilter(query.fulfillmentMode),
channel: normalizeOrderFulfillmentModeFilter(query.channel),
platformOrderId: String(query.platformOrderId || '').trim(),
payStatus: String(query.payStatus || '').trim(),
taskStatus: String(query.taskStatus || '').trim(),
@@ -147,7 +147,7 @@ export async function getAdminOrders(
}
}
const ADMIN_ORDER_FULFILLMENT_MODES = new Set([
const ADMIN_ORDER_FULFILLMENT_CHANNELS = new Set([
'work_order',
'affiliate_dash',
'third_party_voucher',
@@ -162,7 +162,7 @@ function normalizeOrderFulfillmentModeFilter(value: unknown): string {
const mode = String(value || '')
.trim()
.toLowerCase()
return ADMIN_ORDER_FULFILLMENT_MODES.has(mode) ? mode : ''
return ADMIN_ORDER_FULFILLMENT_CHANNELS.has(mode) ? mode : ''
}
function groupRowsByOrderId<T extends { order_id?: number | string | null }>(
@@ -255,7 +255,7 @@ export async function getAdminOrderDetail(
claimStatus: order.claim_status || 'active',
claimStatusReason: order.claim_status_reason || '',
claimStatusUpdatedAt: order.claim_status_updated_at || null,
fulfillmentMode: fulfillment.fulfillmentMode,
channel: fulfillment.channel,
manualItemCount: fulfillment.manualItemCount,
pendingItemCount: fulfillment.pendingItemCount,
manualPendingCount: manualConfig.pending,
@@ -281,7 +281,7 @@ export async function getAdminOrderDetail(
revenueFen,
refundedFen,
refundableFen: Math.max(0, revenueFen - refundedFen),
fulfillmentMode: resolveOrderItemFulfillmentMode(item),
channel: resolveOrderItemFulfillmentMode(item),
deliveryMode: resolveOrderItemDeliveryMode(tasks, item.id),
spec:
item.spec_json && typeof item.spec_json === 'object'