From ce775ca10225208db2c2085ed748c81aa83b942c Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sat, 31 Oct 2026 20:17:28 +0800 Subject: [PATCH] =?UTF-8?q?feat(cost-center):=20=E7=AC=AC=E4=B8=89?= =?UTF-8?q?=E6=96=B9=E5=8D=A1=E5=88=B8=E6=B8=A0=E9=81=93=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E6=8E=A5=E5=85=A5=E6=88=90=E6=9C=AC=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 商品来源筛选补 tpv: 前缀,卡券商品列表不再为空 - 成本模型新增 third_party_voucher_cost,覆盖预览/成本项/概览作用域 - 前后端成本模型枚举与下拉同步 --- .../src/services/cost-center/cost-center-constants.ts | 3 +++ .../src/services/cost-center/cost-center-mappers.ts | 4 ++++ .../services/cost-center/cost-center-overview-service.ts | 2 ++ .../backend/src/services/cost-center/cost-center.test.ts | 9 +++++++++ apps/frontend/src/pages/admin/cost-center/shared.ts | 1 + apps/frontend/src/types/admin/cost-center.ts | 1 + 6 files changed, 20 insertions(+) diff --git a/apps/backend/src/services/cost-center/cost-center-constants.ts b/apps/backend/src/services/cost-center/cost-center-constants.ts index e687ae92..9a947ad7 100644 --- a/apps/backend/src/services/cost-center/cost-center-constants.ts +++ b/apps/backend/src/services/cost-center/cost-center-constants.ts @@ -28,6 +28,7 @@ export type TemplateMappingMatchType = export type CostModel = | 'affiliate_purchase' | 'work_order_reward' + | 'third_party_voucher_cost' | 'manual_actual' | 'platform_fee' | 'payment_fee' @@ -37,6 +38,7 @@ export type CostModel = export const COST_MODELS = new Set([ 'affiliate_purchase', 'work_order_reward', + 'third_party_voucher_cost', 'manual_actual', 'platform_fee', 'payment_fee', @@ -48,6 +50,7 @@ export const COST_MODELS = new Set([ export const COST_MODEL_CODE_BASE: Record = { affiliate_purchase: 'affiliate_purchase', work_order_reward: 'work_order_reward', + third_party_voucher_cost: 'third_party_voucher_cost', manual_actual: 'manual_actual', platform_fee: 'platform_fee', payment_fee: 'payment_fee', diff --git a/apps/backend/src/services/cost-center/cost-center-mappers.ts b/apps/backend/src/services/cost-center/cost-center-mappers.ts index 9e37d6c3..24d0bffe 100644 --- a/apps/backend/src/services/cost-center/cost-center-mappers.ts +++ b/apps/backend/src/services/cost-center/cost-center-mappers.ts @@ -38,6 +38,7 @@ export function productSourceFilterSql(paramIndex: number, column = 'p.canonical return `( ($${paramIndex} = 'affiliate_dash' AND ${column} LIKE 'aff:%') OR ($${paramIndex} = 'work_order' AND (${column} LIKE 'tpl:%' OR ${column} LIKE 'wo:%')) + OR ($${paramIndex} = 'third_party_voucher' AND ${column} LIKE 'tpv:%') OR ($${paramIndex} = 'manual' AND ${column} LIKE 'man:%') )` } @@ -52,6 +53,7 @@ export function costModelAppliesTo(costModel: string, fulfillmentMode: string) { if ((GENERIC_COST_MODELS as readonly string[]).includes(costModel)) return true if (costModel === 'affiliate_purchase') return fulfillmentMode === 'affiliate_dash' if (costModel === 'work_order_reward') return fulfillmentMode === 'work_order' + if (costModel === 'third_party_voucher_cost') return fulfillmentMode === 'third_party_voucher' if (costModel === 'manual_actual') return fulfillmentMode === 'manual' return false } @@ -67,6 +69,8 @@ export function costModelScopeSql(fulfillmentModeExpression: string): string { AND ${fulfillmentModeExpression} = 'affiliate_dash') OR (ci.cost_model = 'work_order_reward' AND ${fulfillmentModeExpression} = 'work_order') + OR (ci.cost_model = 'third_party_voucher_cost' + AND ${fulfillmentModeExpression} = 'third_party_voucher') OR (ci.cost_model = 'manual_actual' AND ${fulfillmentModeExpression} = 'manual') )` diff --git a/apps/backend/src/services/cost-center/cost-center-overview-service.ts b/apps/backend/src/services/cost-center/cost-center-overview-service.ts index bcc78e49..3b56cd0c 100644 --- a/apps/backend/src/services/cost-center/cost-center-overview-service.ts +++ b/apps/backend/src/services/cost-center/cost-center-overview-service.ts @@ -41,6 +41,7 @@ export async function getAdminCostCenterOverview(params: { scope?: string } = {} $1::text IS NULL OR ($1 = 'affiliate_dash' AND p.canonical_sku LIKE 'aff:%') OR ($1 = 'work_order' AND (p.canonical_sku LIKE 'tpl:%' OR p.canonical_sku LIKE 'wo:%')) + OR ($1 = 'third_party_voucher' AND p.canonical_sku LIKE 'tpv:%') OR ($1 = 'manual' AND p.canonical_sku LIKE 'man:%') ) ), @@ -48,6 +49,7 @@ export async function getAdminCostCenterOverview(params: { scope?: string } = {} SELECT CASE $1 WHEN 'affiliate_dash' THEN 'affiliate_purchase' WHEN 'work_order' THEN 'work_order_reward' + WHEN 'third_party_voucher' THEN 'third_party_voucher_cost' WHEN 'manual' THEN 'manual_actual' END AS cost_model ) diff --git a/apps/backend/src/services/cost-center/cost-center.test.ts b/apps/backend/src/services/cost-center/cost-center.test.ts index 832575d7..030ec22f 100644 --- a/apps/backend/src/services/cost-center/cost-center.test.ts +++ b/apps/backend/src/services/cost-center/cost-center.test.ts @@ -1,6 +1,7 @@ import assert from 'node:assert/strict' import test from 'node:test' +import { costModelAppliesTo } from './cost-center-mappers.js' import { normalizeMappingMatchType, normalizeProviderKey, @@ -8,6 +9,7 @@ import { } from './cost-center-shared.js' import { calculateCostTotal, validateCostVersionScope } from './cost-resolution-service.js' import { resolveMappingProductId } from './product-catalog-service.js' +import { productSourceFilterSql } from './cost-center-mappers.js' import { buildTemplateMappingPlan, buildTemplateProductSku, @@ -289,3 +291,10 @@ test('接单模板改价 SQL 的参数必须显式指定 bigint', async () => { ), ) }) + +test('成本模型与商品来源必须覆盖第三方卡券渠道', () => { + assert.equal(costModelAppliesTo('third_party_voucher_cost', 'third_party_voucher'), true) + assert.equal(costModelAppliesTo('third_party_voucher_cost', 'manual'), false) + assert.ok(productSourceFilterSql(1).includes("'third_party_voucher'")) + assert.ok(productSourceFilterSql(1).includes("'tpv:%'")) +}) diff --git a/apps/frontend/src/pages/admin/cost-center/shared.ts b/apps/frontend/src/pages/admin/cost-center/shared.ts index 2d873990..402a0a36 100644 --- a/apps/frontend/src/pages/admin/cost-center/shared.ts +++ b/apps/frontend/src/pages/admin/cost-center/shared.ts @@ -78,6 +78,7 @@ export type CostVersionFormValues = { export const COST_MODEL_OPTIONS = [ { value: 'affiliate_purchase', label: '上游采购成本' }, { value: 'work_order_reward', label: '接单平台打手报酬' }, + { value: 'third_party_voucher_cost', label: '第三方卡券采购成本' }, { value: 'manual_actual', label: '人工客服实际支出' }, { value: 'platform_fee', label: '平台服务费' }, { value: 'payment_fee', label: '支付通道费' }, diff --git a/apps/frontend/src/types/admin/cost-center.ts b/apps/frontend/src/types/admin/cost-center.ts index f44ddda8..b817abd4 100644 --- a/apps/frontend/src/types/admin/cost-center.ts +++ b/apps/frontend/src/types/admin/cost-center.ts @@ -18,6 +18,7 @@ export type ProductSource = 'affiliate_dash' | 'work_order' | 'third_party_vouch export type CostModel = | 'affiliate_purchase' | 'work_order_reward' + | 'third_party_voucher_cost' | 'manual_actual' | 'platform_fee' | 'payment_fee'