From d9bbd94366dbd513eda45de6d857cf272128f3c4 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Wed, 12 Aug 2026 12:47:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gateway/src/main/resources/application.yml | 2 +- .../finance-full-fixtures.mjs | 4 ++++ ...inance-reconciliation-full-contract.test.mjs | 11 +++++++++-- .../finance-reconciliation-full.mjs | 17 +++++++++++++++-- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/gateway/src/main/resources/application.yml b/gateway/src/main/resources/application.yml index 7944607e4..97a833a02 100644 --- a/gateway/src/main/resources/application.yml +++ b/gateway/src/main/resources/application.yml @@ -77,7 +77,7 @@ spring: - id: firstleg-service uri: lb://firstleg-service predicates: - - Path=/api/merchant/v1/first-leg/**,/api/merchant/v1/customs/**,/api/platform/v1/first-leg/**,/api/platform/v1/customs/**,/api/platform/v1/port-schedules/**,/api/platform/v1/carrier-schedule-queries/**,/api/platform/v1/containers/**,/api/platform/v1/ocean-bookings/**,/api/platform/v1/truck-appointments/**,/api/platform/v1/fba-pricing/**,/api/platform/v1/inbounds/**,/api/platform/v1/fee-items/**,/api/platform/inbounds/**,/openapi/v1/inbound-measurements/** + - Path=/api/merchant/v1/first-leg/**,/api/merchant/v1/customs/**,/api/platform/v1/first-leg/**,/api/platform/v1/customs/**,/api/platform/v1/port-schedules/**,/api/platform/v1/carrier-schedule-queries/**,/api/platform/v1/containers/**,/api/platform/v1/ocean-bookings/**,/api/platform/v1/truck-appointments/**,/api/platform/v1/fba-pricing/**,/api/platform/v1/inbounds/**,/api/platform/v1/fee-items/**,/api/platform/v1/loading-plans/**,/api/platform/inbounds/**,/openapi/v1/inbound-measurements/** - id: finance-service uri: lb://finance-service predicates: diff --git a/scripts/admin-test-env-modules/finance-full-fixtures.mjs b/scripts/admin-test-env-modules/finance-full-fixtures.mjs index 9e31a7593..e0c0e27e5 100644 --- a/scripts/admin-test-env-modules/finance-full-fixtures.mjs +++ b/scripts/admin-test-env-modules/finance-full-fixtures.mjs @@ -328,6 +328,10 @@ export async function run(context) { const path = `${basePath}?accountId=${encodeURIComponent(container.tenantId)}` const body = { feeItemId: Number(feeItems.PAYABLE.id), direction: 'PAYABLE', businessDate: dateOffset(-2), amount: 300, currency: 'CNY', settlementIncluded: true, remark: `${context.marker}-CONTAINER_SHARED` } const response = await request(context, 'POST', path, body) + if (response?.http === 404) { + blocker(context, '按柜费用来源接口已部署并可路由', + `POST ${path} returned HTTP 404 before Controller handling; Firstleg Controller contract is /api/platform/v1/loading-plans/{loadingPlanId}/fee-details, but current Gateway route does not expose /api/platform/v1/loading-plans/**; requestId=${response.requestId || 'missing'}, traceId=${response.traceId || 'missing'}`) + } const detail = context.apiSuccess(MODULE, '创建按柜费用来源', response, '', OPTIONS) const feeId = id(detail?.id) if (feeId) { const approval = await request(context, 'POST', `${basePath}/${feeId}/approve?accountId=${encodeURIComponent(container.tenantId)}`); context.apiSuccess(MODULE, '审核按柜费用来源', approval, '', OPTIONS) } diff --git a/scripts/admin-test-env-modules/finance-reconciliation-full-contract.test.mjs b/scripts/admin-test-env-modules/finance-reconciliation-full-contract.test.mjs index 385dc1794..aa3b68a07 100644 --- a/scripts/admin-test-env-modules/finance-reconciliation-full-contract.test.mjs +++ b/scripts/admin-test-env-modules/finance-reconciliation-full-contract.test.mjs @@ -22,9 +22,13 @@ function fakeContext(withFixture = true) { ] const feeSources = [...receivables, { feeSourceId: '105', orderId: '205', direction: 'PAYABLE', currency: 'CNY', - businessDate: '2026-08-11', partnerId: '501', partnerName: 'Fixture Partner' }] + businessDate: '2026-08-11', partnerId: '501', partnerName: 'Fixture Partner' }, + { feeSourceId: null, sourceType: 'MANUAL', direction: 'PAYABLE', currency: 'CNY', + businessDate: '2026-08-10', billingBasis: 'CONTAINER_SHARED', partnerId: '501', + containerBookingId: 'CB-FAILED' }] const fixture = { marker, customerId: '11', - orders: feeSources.map((item) => ({ orderId: item.orderId, shipmentId: `SHIP-${item.orderId}` })), + orders: feeSources.filter((item) => item.orderId) + .map((item) => ({ orderId: item.orderId, shipmentId: `SHIP-${item.orderId}` })), feeSources: feeSources.map((item) => ({ ...item, marker })) } const statements = new Map() const settlements = new Map() @@ -152,6 +156,9 @@ test('run 发布一条日期链、三张独立已出账单、三张 AR 和一张 assert.equal(fixture.payableSettlements[0].supplierId, '501') assert.equal(fixture.payableSettlements[0].partnerId, '501') assert.equal(fixture.payableSettlements[0].supplierName, 'Fixture Partner') + assert.equal(context.coverageGaps.some((gap) => gap.category === 'uncreated-fee-source-attempt' + && gap.reason.includes('containerBookingId=CB-FAILED')), true) + assert.equal(context.calls.some((call) => call.body?.feeSourceIds?.includes(null)), false) assert.equal(context.calls.some((call) => call.path.includes('/fee-pool') || call.body?.generationMode === 'BY_CUTOFF'), false) assert.equal(context.steps.filter((step) => !step.passed).length, 0) diff --git a/scripts/admin-test-env-modules/finance-reconciliation-full.mjs b/scripts/admin-test-env-modules/finance-reconciliation-full.mjs index bd545dbdf..de11d3b83 100644 --- a/scripts/admin-test-env-modules/finance-reconciliation-full.mjs +++ b/scripts/admin-test-env-modules/finance-reconciliation-full.mjs @@ -54,7 +54,7 @@ function normalizeFixture(context) { || !Array.isArray(root.orders) || !Array.isArray(root.feeSources)) return null const customerId = id(root.customerId) const orders = new Map(root.orders.map((order) => [id(order.orderId ?? order.id), order])) - const sources = root.feeSources.map((raw) => { + const attemptedSources = root.feeSources.map((raw) => { const order = orders.get(id(raw.orderId)) return { raw, feeSourceId: sourceId(raw), customerId: id(raw.customerId ?? customerId), @@ -65,7 +65,11 @@ function normalizeFixture(context) { supplierName: raw.supplierName ?? raw.partnerName ?? root.supplierName ?? root.partnerName ?? null, } }) - return { root, customerId, sources } + return { + root, customerId, + sources: attemptedSources.filter((source) => source.feeSourceId), + uncreatedSources: attemptedSources.filter((source) => !source.feeSourceId), + } } async function createStatement(context, sources, action, name, generationMode = 'SELECTED') { @@ -178,6 +182,15 @@ export async function run(context) { finish(context, start) return } + if (fixture.uncreatedSources.length > 0) { + const evidence = fixture.uncreatedSources.map((source) => { + const raw = source.raw + return `sourceType=${raw.sourceType ?? 'missing'}, billingBasis=${raw.billingBasis ?? 'missing'}, containerBookingId=${id(raw.containerBookingId) || 'missing'}` + }).join('; ') + context.addCoverageGap(MODULE, SCENARIO, '忽略未创建的费用来源尝试', + `Finance 未返回 feeSourceId,不得作为费用来源消费:${evidence}`, + 'uncreated-fee-source-attempt', { marker: context.marker, count: fixture.uncreatedSources.length }) + } const invalid = fixture.sources.filter((source) => !source.feeSourceId || !source.customerId || !source.businessDate || !source.waybillId || source.marker !== context.marker) if (invalid.length > 0) {