From 19cb31246540719fc729b718a9003c8c16d702ab Mon Sep 17 00:00:00 2001 From: jacy <18049821889@163.com> Date: Sun, 26 Jul 2026 09:51:33 +0800 Subject: [PATCH] =?UTF-8?q?=E7=89=A9=E6=B5=81=E5=AF=B9=E8=B4=A6=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/admin-web/src/App.tsx | 2 + apps/admin-web/src/layouts/AdminLayout.tsx | 2 + .../src/pages/FulfillmentProvidersPage.tsx | 109 ++- .../src/pages/LogisticsBillsPage.tsx | 625 ++++++++++++++++++ packages/domain/src/index.test.ts | 32 + packages/domain/src/index.ts | 41 ++ packages/shared-types/src/enums.ts | 11 + .../shared-types/src/fulfillment-provider.ts | 37 +- packages/shared-types/src/settlement.ts | 49 ++ server/dukang-api/prisma/schema.prisma | 100 ++- server/dukang-api/prisma/seed-v31.ts | 8 + .../hq-operation/hq-operation.constants.ts | 6 + .../src/jobs/settlement.scheduler.ts | 10 +- .../fulfillment-provider.service.ts | 180 ++++- .../admin-fulfillment-providers.controller.ts | 24 + .../src/modules/ops/dto/admin-mutate.dto.ts | 69 ++ .../settlement/settlement.controller.ts | 80 +++ .../modules/settlement/settlement.module.ts | 5 +- .../modules/settlement/settlement.service.ts | 513 +++++++++++++- 杜康好客-v3-PRD.md | 15 +- 杜康好客-知识库.md | 18 +- 21 files changed, 1909 insertions(+), 27 deletions(-) create mode 100644 apps/admin-web/src/pages/LogisticsBillsPage.tsx diff --git a/apps/admin-web/src/App.tsx b/apps/admin-web/src/App.tsx index fc7b7cd..6d653a4 100644 --- a/apps/admin-web/src/App.tsx +++ b/apps/admin-web/src/App.tsx @@ -31,6 +31,7 @@ import ResourcesPage from './pages/ResourcesPage'; import StoreBillsPage from './pages/StoreBillsPage'; import PartnerBillsPage from './pages/PartnerBillsPage'; import WineryBillsPage from './pages/WineryBillsPage'; +import LogisticsBillsPage from './pages/LogisticsBillsPage'; import TicketsPage from './pages/TicketsPage'; import SupportTicketsPage from './pages/SupportTicketsPage'; import InvoicesPage from './pages/InvoicesPage'; @@ -95,6 +96,7 @@ export default function App() { } /> } /> } /> + } /> } /> } /> } /> diff --git a/apps/admin-web/src/layouts/AdminLayout.tsx b/apps/admin-web/src/layouts/AdminLayout.tsx index 74efdd4..726ac90 100644 --- a/apps/admin-web/src/layouts/AdminLayout.tsx +++ b/apps/admin-web/src/layouts/AdminLayout.tsx @@ -75,6 +75,7 @@ const MENU_ITEMS: MenuProps['items'] = [ { key: '/finance/store-bills', label: '门店账单' }, { key: '/finance/partner-bills', label: '合伙人账单' }, { key: '/finance/winery-bills', label: '酒厂账单' }, + { key: '/finance/logistics-bills', label: '物流对账' }, ], }, { @@ -156,6 +157,7 @@ function menuAllowed(key: string, permissionKeys: string[]): boolean { '/finance/store-bills': 'finance', '/finance/partner-bills': 'finance', '/finance/winery-bills': 'finance', + '/finance/logistics-bills': 'finance', 'benefit-group': 'benefit', '/benefit/coupons': 'benefit', '/benefit/ledgers': 'benefit', diff --git a/apps/admin-web/src/pages/FulfillmentProvidersPage.tsx b/apps/admin-web/src/pages/FulfillmentProvidersPage.tsx index 588196c..e53b4ef 100644 --- a/apps/admin-web/src/pages/FulfillmentProvidersPage.tsx +++ b/apps/admin-web/src/pages/FulfillmentProvidersPage.tsx @@ -2,8 +2,10 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { Alert, Button, + Divider, Form, Input, + InputNumber, Modal, Select, Space, @@ -14,10 +16,13 @@ import { } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { + DEFAULT_XFX_LOGISTICS_PRICING, FULFILLMENT_PROVIDER_STATUS_LABELS, FULFILLMENT_PROVIDER_TYPE_LABELS, FulfillmentProviderStatus, FulfillmentProviderType, + LOGISTICS_SETTLEMENT_METHOD_LABELS, + LogisticsSettlementMethod, isXfxProviderCode, type FulfillmentProviderDto, } from '@dukang/shared-types'; @@ -32,6 +37,9 @@ const STATUS_OPTIONS = Object.entries(FULFILLMENT_PROVIDER_STATUS_LABELS).map(([ value, label, })); +const SETTLEMENT_OPTIONS = Object.entries(LOGISTICS_SETTLEMENT_METHOD_LABELS).map( + ([value, label]) => ({ value, label }), +); const SIGN_OPTIONS = [ { value: 'MD5', label: 'MD5' }, { value: 'HMAC-SHA256', label: 'HMAC-SHA256' }, @@ -77,6 +85,12 @@ export default function FulfillmentProvidersPage() { status: FulfillmentProviderStatus.ACTIVE, apiUrl: DEFAULT_XFX_API_URL, signType: 'MD5', + settlementMethod: LogisticsSettlementMethod.PREPAID, + baseBottles: DEFAULT_XFX_LOGISTICS_PRICING.baseBottles, + baseFee: DEFAULT_XFX_LOGISTICS_PRICING.baseFee, + extraBottleFee: DEFAULT_XFX_LOGISTICS_PRICING.extraBottleFee, + boxBottles: DEFAULT_XFX_LOGISTICS_PRICING.boxBottles, + boxFee: DEFAULT_XFX_LOGISTICS_PRICING.boxFee, }); setOpen(true); } @@ -84,6 +98,7 @@ export default function FulfillmentProvidersPage() { function openEdit(row: FulfillmentProviderDto) { setEditRow(row); const xfx = row.xiaofeixiaConfig; + const pricing = row.pricingRules; form.setFieldsValue({ code: row.code, name: row.name, @@ -94,6 +109,16 @@ export default function FulfillmentProvidersPage() { apiKey: '', signType: xfx?.signType || 'MD5', appId: xfx?.appId || '', + bankAccountName: row.bankAccountName || '', + bankName: row.bankName || '', + bankBranch: row.bankBranch || '', + bankAccountNo: row.bankAccountNo || '', + settlementMethod: row.settlementMethod || LogisticsSettlementMethod.PREPAID, + baseBottles: pricing?.baseBottles ?? DEFAULT_XFX_LOGISTICS_PRICING.baseBottles, + baseFee: pricing?.baseFee ?? DEFAULT_XFX_LOGISTICS_PRICING.baseFee, + extraBottleFee: pricing?.extraBottleFee ?? DEFAULT_XFX_LOGISTICS_PRICING.extraBottleFee, + boxBottles: pricing?.boxBottles ?? DEFAULT_XFX_LOGISTICS_PRICING.boxBottles, + boxFee: pricing?.boxFee ?? DEFAULT_XFX_LOGISTICS_PRICING.boxFee, }); setOpen(true); } @@ -104,6 +129,18 @@ export default function FulfillmentProvidersPage() { name: v.name, type: v.type, status: v.status, + bankAccountName: v.bankAccountName || null, + bankName: v.bankName || null, + bankBranch: v.bankBranch || null, + bankAccountNo: v.bankAccountNo || null, + settlementMethod: v.settlementMethod, + pricingRules: { + baseBottles: Number(v.baseBottles), + baseFee: Number(v.baseFee), + extraBottleFee: Number(v.extraBottleFee), + boxBottles: v.boxBottles != null ? Number(v.boxBottles) : undefined, + boxFee: v.boxFee != null ? Number(v.boxFee) : undefined, + }, }; if (isXfxProviderCode(String(v.code)) && v.type === FulfillmentProviderType.API) { @@ -144,6 +181,19 @@ export default function FulfillmentProvidersPage() { dataIndex: 'type', render: (v) => FULFILLMENT_PROVIDER_TYPE_LABELS[v as FulfillmentProviderType] || v, }, + { + title: '结算', + dataIndex: 'settlementMethod', + width: 100, + render: (v) => + LOGISTICS_SETTLEMENT_METHOD_LABELS[v as LogisticsSettlementMethod] || v || '—', + }, + { + title: '充值余额', + dataIndex: 'prepaidBalance', + width: 100, + render: (v) => `¥${Number(v || 0).toFixed(2)}`, + }, { title: '状态', dataIndex: 'status', @@ -188,7 +238,7 @@ export default function FulfillmentProvidersPage() { 仓配管理 - 注册第三方履约接口并填写凭证;仓库绑定后,推单将使用此处配置的 API 地址与密钥 + 注册承运商接口凭证,并配置物流对账用的银行账户、结算方式与计价标准