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
@@ -69,7 +69,7 @@ type ConfirmContextRow = {
revenue_fen: string
basis_at: string
canonical_product_id: number | null
fulfillment_mode: string
channel: string
config_id: number | null
platform_key: string
credit_quantity: string
@@ -225,7 +225,7 @@ export async function confirmManualFulfillment(payload: JsonObject, session: Adm
COALESCE(oi.revenue_amount_fen, 0)::bigint AS revenue_fen,
COALESCE(o.paid_at, o.created_at) AS basis_at,
oi.canonical_product_id,
COALESCE(oi.fulfillment_channel, CASE WHEN oi.fulfillment_status = 'archived' THEN 'archived' ELSE 'pending' END) AS fulfillment_mode,
COALESCE(oi.fulfillment_channel, CASE WHEN oi.fulfillment_status = 'archived' THEN 'archived' ELSE 'pending' END) AS channel,
oi.fulfillment_config_id AS config_id,
COALESCE(cfg.platform_key, '') AS platform_key,
COALESCE(cfg.credit_quantity, 0)::bigint AS credit_quantity,
@@ -256,7 +256,7 @@ export async function confirmManualFulfillment(payload: JsonObject, session: Adm
errorCode: 'manual_fulfillment_order_item_not_found',
})
}
if (row.fulfillment_mode !== 'manual') {
if (row.channel !== 'manual') {
throw createHttpError('该订单项不是人工履约', {
statusCode: 409,
errorCode: 'manual_fulfillment_not_manual',
@@ -544,7 +544,7 @@ async function createManualItem(
const orderId = Number(order.rows[0]?.id)
const item = await query(
`INSERT INTO order_items (
order_id, sku_code, sku_name, fulfillment_mode, spec_json,
order_id, sku_code, sku_name, channel, spec_json,
revenue_amount_fen, revenue_basis, created_at, updated_at
) VALUES ($1, $2, $3, 'manual', $4::jsonb, 5000, 'order_total', NOW(), NOW())
RETURNING id`,
@@ -10,7 +10,7 @@ import { nowIso } from '../../utils/time.js'
* 2. 归档订单(archived)不进事实;
* 3. 成本来自 `cost_snapshots`,缺失就记 0 并计数,不把 0 当真实成本。
*
* 事实表暂时沿用 `fulfillment_mode` 列承载渠道值(113 再改列名)。
* 事实表暂时沿用 `channel` 列承载渠道值(113 再改列名)。
*/
export type ChannelFactsResult = {
@@ -107,7 +107,7 @@ export async function rebuildChannelSalesDailyFacts(input: {
) c ON c.order_item_id = s.order_item_id
)
INSERT INTO sales_daily_facts (
stat_date, provider, shop_id, canonical_product_id, fulfillment_mode,
stat_date, provider, shop_id, canonical_product_id, channel,
order_count, item_count, quantity,
gross_revenue_fen, refund_amount_fen, net_revenue_fen,
cost_amount_fen, cost_missing_count, cost_stage,
@@ -18,7 +18,7 @@ type SalesFactRow = {
canonical_product_id: number | null
canonical_sku: string | null
canonical_name: string | null
fulfillment_mode: string
channel: string
order_count: number
item_count: number
quantity: string | number
@@ -99,7 +99,7 @@ export async function getSalesProfitOverview(input: SalesFactsInput = {}) {
[range.fromDate, range.toDate],
),
query<{
fulfillment_mode: string
channel: string
item_count: number
gross_revenue_fen: number
cost_amount_fen: number
@@ -108,7 +108,7 @@ export async function getSalesProfitOverview(input: SalesFactsInput = {}) {
}>(
`
SELECT
fulfillment_mode,
channel,
SUM(item_count)::int AS item_count,
SUM(gross_revenue_fen)::bigint AS gross_revenue_fen,
SUM(cost_amount_fen)::bigint AS cost_amount_fen,
@@ -116,7 +116,7 @@ export async function getSalesProfitOverview(input: SalesFactsInput = {}) {
SUM(gross_profit_fen)::bigint AS gross_profit_fen
FROM sales_daily_facts
WHERE stat_date BETWEEN $1::date AND $2::date
GROUP BY fulfillment_mode
GROUP BY channel
ORDER BY gross_revenue_fen DESC
`,
[range.fromDate, range.toDate],
@@ -210,8 +210,8 @@ export async function getSalesProfitOverview(input: SalesFactsInput = {}) {
costMissingCount: Number(row.cost_missing_count || 0),
grossProfitFen: Number(row.gross_profit_fen || 0),
})),
byFulfillmentMode: carrierResult.rows.map((row) => ({
fulfillmentMode: row.fulfillment_mode,
byChannel: carrierResult.rows.map((row) => ({
channel: row.channel,
itemCount: Number(row.item_count || 0),
grossRevenueFen: Number(row.gross_revenue_fen || 0),
costAmountFen: Number(row.cost_amount_fen || 0),
@@ -239,9 +239,7 @@ export async function listSalesDailyFacts(input: Record<string, unknown> = {}) {
normalizePositiveInteger(input.pageSize ?? input.page_size, 20, '每页数量'),
)
const provider = String(input.provider || '').trim()
const fulfillmentMode = String(
input.fulfillmentMode || input.fulfillment_mode || input.carrierType || '',
).trim()
const channel = String(input.channel || input.channel || input.carrierType || '').trim()
const canonicalProductId = Number(input.canonicalProductId || input.canonical_product_id || 0)
const conditions = ['f.stat_date BETWEEN $1::date AND $2::date']
@@ -250,9 +248,9 @@ export async function listSalesDailyFacts(input: Record<string, unknown> = {}) {
params.push(provider)
conditions.push(`f.provider = $${params.length}`)
}
if (fulfillmentMode) {
params.push(fulfillmentMode)
conditions.push(`f.fulfillment_mode = $${params.length}`)
if (channel) {
params.push(channel)
conditions.push(`f.channel = $${params.length}`)
}
if (Number.isInteger(canonicalProductId) && canonicalProductId > 0) {
params.push(canonicalProductId)
@@ -301,7 +299,7 @@ function mapSalesFact(row: SalesFactRow) {
canonicalProductId: row.canonical_product_id ? Number(row.canonical_product_id) : null,
canonicalSku: row.canonical_sku || '',
canonicalName: row.canonical_name || '未归一',
fulfillmentMode: row.fulfillment_mode,
channel: row.channel,
orderCount: Number(row.order_count || 0),
itemCount: Number(row.item_count || 0),
quantity: Number(row.quantity || 0),