From 4bdb09068ce942d4709ce0366d2c8114ce801238 Mon Sep 17 00:00:00 2001 From: jacy <18049821889@163.com> Date: Mon, 7 Sep 2026 15:56:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(store):=20v4.0.18=20=E9=97=A8=E5=BA=97?= =?UTF-8?q?=E5=A4=9A=E6=94=B6=E6=AC=BE=E8=B4=A6=E6=88=B7=E4=B8=8E=E5=AD=90?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E7=BB=A7=E6=89=BF=E4=BA=8C=E7=BB=B4=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit C 端门店列表拼接省市区县地址;门店多银行账户与默认打款账户;子账号独立 sa_ 关联码及统计维度;同步 v4.0.18 开发文档。 Co-authored-by: Cursor --- .../src/components/StoreBankAccountsPanel.tsx | 178 +++++++++++++ apps/admin-web/src/pages/StoresPage.tsx | 9 + apps/h5-partner/src/pages/AssocQrcodePage.tsx | 5 +- apps/h5-partner/src/pages/UsersManagePage.tsx | 4 +- apps/h5-shop/src/App.tsx | 2 + apps/h5-shop/src/pages/BankAccountsPage.tsx | 252 ++++++++++++++++++ apps/h5-shop/src/pages/MinePage.tsx | 6 + apps/h5-shop/src/styles.css | 12 + apps/mini-user/src/lib/promo.ts | 2 +- apps/mini-user/src/lib/store-display.ts | 16 ++ apps/mini-user/src/pages/stores/index.tsx | 11 +- docs/杜康好客-v4-PRD.md | 17 +- docs/杜康好客-v4-现状对照.md | 6 +- docs/杜康好客-v4.0.18-开发文档.md | 106 ++++++++ packages/shared-types/src/partner-assoc.ts | 6 + packages/shared-types/src/settlement.ts | 19 ++ .../migrate-store-bank-account-v4018.sql | 43 +++ .../migrate-user-assoc-sub-account-v4018.sql | 5 + server/dukang-api/prisma/schema.prisma | 24 ++ .../src/common/store/store-bank.util.ts | 107 ++++++-- .../src/modules/ops/admin-redeem.service.ts | 4 +- .../modules/settlement/settlement.service.ts | 41 ++- .../modules/store/partner-assoc.service.ts | 198 ++++++++++---- .../modules/store/store-bank.controller.ts | 93 +++++++ .../src/modules/store/store-bank.service.ts | 121 +++++++++ .../src/modules/store/store.module.ts | 8 +- 26 files changed, 1193 insertions(+), 102 deletions(-) create mode 100644 apps/admin-web/src/components/StoreBankAccountsPanel.tsx create mode 100644 apps/h5-shop/src/pages/BankAccountsPage.tsx create mode 100644 docs/杜康好客-v4.0.18-开发文档.md create mode 100644 server/dukang-api/prisma/migrate-store-bank-account-v4018.sql create mode 100644 server/dukang-api/prisma/migrate-user-assoc-sub-account-v4018.sql create mode 100644 server/dukang-api/src/modules/store/store-bank.controller.ts create mode 100644 server/dukang-api/src/modules/store/store-bank.service.ts diff --git a/apps/admin-web/src/components/StoreBankAccountsPanel.tsx b/apps/admin-web/src/components/StoreBankAccountsPanel.tsx new file mode 100644 index 0000000..123b377 --- /dev/null +++ b/apps/admin-web/src/components/StoreBankAccountsPanel.tsx @@ -0,0 +1,178 @@ +import { useCallback, useEffect, useState } from 'react'; +import { Button, Form, Input, Modal, Popconfirm, Space, Table, Tag, message } from 'antd'; +import type { ColumnsType } from 'antd/es/table'; +import type { StoreBankAccountDto } from '@dukang/shared-types'; +import { request } from '../lib/api'; + +type Props = { storeId: string }; + +type FormValues = { bankAccountName: string; bankAccountNo: string; bankBranch?: string }; + +export default function StoreBankAccountsPanel({ storeId }: Props) { + const [form] = Form.useForm(); + const [items, setItems] = useState([]); + const [loading, setLoading] = useState(false); + const [editingId, setEditingId] = useState(null); + const [modalOpen, setModalOpen] = useState(false); + const [submitting, setSubmitting] = useState(false); + + const load = useCallback(async () => { + setLoading(true); + try { + const res = await request(`/admin/stores/${storeId}/bank-accounts`); + setItems(res ?? []); + } catch (e) { + message.error(e instanceof Error ? e.message : '加载失败'); + } finally { + setLoading(false); + } + }, [storeId]); + + useEffect(() => { + void load(); + }, [load]); + + function openCreate() { + setEditingId(null); + form.resetFields(); + setModalOpen(true); + } + + function openEdit(item: StoreBankAccountDto) { + setEditingId(item.id); + form.setFieldsValue({ + bankAccountName: item.bankAccountName, + bankAccountNo: item.bankAccountNo, + bankBranch: item.bankBranch ?? '', + }); + setModalOpen(true); + } + + async function submit() { + const v = await form.validateFields(); + setSubmitting(true); + try { + const body = JSON.stringify({ + bankAccountName: v.bankAccountName.trim(), + bankAccountNo: v.bankAccountNo.replace(/\s+/g, ''), + bankBranch: v.bankBranch?.trim() || undefined, + }); + if (editingId) { + await request(`/admin/stores/${storeId}/bank-accounts/${editingId}`, { method: 'PUT', body }); + } else { + await request(`/admin/stores/${storeId}/bank-accounts`, { method: 'POST', body }); + } + message.success('已保存'); + setModalOpen(false); + void load(); + } catch (e) { + message.error(e instanceof Error ? e.message : '保存失败'); + } finally { + setSubmitting(false); + } + } + + async function setDefault(id: string) { + try { + await request(`/admin/stores/${storeId}/bank-accounts/${id}/default`, { method: 'POST' }); + message.success('已设为默认打款账户'); + void load(); + } catch (e) { + message.error(e instanceof Error ? e.message : '设置失败'); + } + } + + async function remove(id: string) { + try { + await request(`/admin/stores/${storeId}/bank-accounts/${id}`, { method: 'DELETE' }); + message.success('已删除'); + void load(); + } catch (e) { + message.error(e instanceof Error ? e.message : '删除失败'); + } + } + + const columns: ColumnsType = [ + { title: '收款人', dataIndex: 'bankAccountName', width: 140 }, + { title: '银行账号', dataIndex: 'bankAccountNo', width: 180 }, + { title: '开户行', dataIndex: 'bankBranch', render: (v) => v || '—' }, + { + title: '默认', + dataIndex: 'isDefault', + width: 80, + render: (v) => (v ? 默认 : null), + }, + { + title: '操作', + width: 220, + render: (_, row) => ( + + + {!row.isDefault ? ( + + ) : null} + {!row.isDefault ? ( + void remove(row.id)} + > + + + ) : null} + + ), + }, + ]; + + return ( +
+ + + 每门店可维护多个收款账户,标记「默认」的账户用于核销结算与提现打款;切换即时生效、无需重启服务。 + + + + + setModalOpen(false)} + onOk={() => void submit()} + okText="保存" + > +
+ + + + + + + + + + +
+ + ); +} diff --git a/apps/admin-web/src/pages/StoresPage.tsx b/apps/admin-web/src/pages/StoresPage.tsx index 14007b7..29242c4 100644 --- a/apps/admin-web/src/pages/StoresPage.tsx +++ b/apps/admin-web/src/pages/StoresPage.tsx @@ -51,6 +51,7 @@ import TencentLocPickerModal from '../components/TencentLocPickerModal'; import AdminStorePackagesSection, { type AdminStorePackagesHandle, } from '../components/AdminStorePackagesSection'; +import StoreBankAccountsPanel from '../components/StoreBankAccountsPanel'; import StorePackageAuditPanel, { auditStorePackageRequest, } from '../components/StorePackageAuditPanel'; @@ -1297,6 +1298,14 @@ export default function StoresPage() { ), }, + { + key: 'bank-accounts', + label: '收款账户', + forceRender: true, + children: ( + + ), + }, { key: 'media', label: '审核材料', diff --git a/apps/h5-partner/src/pages/AssocQrcodePage.tsx b/apps/h5-partner/src/pages/AssocQrcodePage.tsx index 9983b99..9b29a9d 100644 --- a/apps/h5-partner/src/pages/AssocQrcodePage.tsx +++ b/apps/h5-partner/src/pages/AssocQrcodePage.tsx @@ -61,7 +61,9 @@ export default function AssocQrcodePage() {

- 用户扫码后首次锁定本合伙人,后续酒单按订单佣金结算 + {summary?.isSubAccount + ? '子账号专属二维码:用户扫码后锁定主账号(佣金归主账号),额外计入本子账号统计' + : '用户扫码后首次锁定本合伙人,后续酒单按订单佣金结算'}

{summary?.qrcodeUrl ? ( 已关联 {summary?.userCount ?? 0} 人 + {typeof summary?.scanCount === 'number' ? ` · 已扫码 ${summary.scanCount} 次` : ''}

+

收款账户

+ + +
+

+ 用于总部打款,可维护多个账户;标记「默认」的账户用于提现打款,切换即时生效、无需重启。 +

+ + {loading ? ( +

加载中…

+ ) : items.length === 0 ? ( +

暂无收款账户{isPrimary ? ',请新增' : ''}

+ ) : ( +
+ {items.map((it) => ( +
+
+
+
+ 收款人 + {it.bankAccountName} + {it.isDefault ? ( + 默认 + ) : null} +
+

账号: {it.bankAccountNo}

+ {it.bankBranch ?

开户行: {it.bankBranch}

: null} +
+
+ {isPrimary ? ( +
+ + {!it.isDefault ? ( + + ) : null} + {!it.isDefault ? ( + + ) : null} +
+ ) : null} +
+ ))} +
+ )} + + {isPrimary ? ( +
+

+ {editing ? '编辑账户' : '新增账户'} +

+
+ + setForm({ ...form, bankAccountName: e.target.value })} + /> + {fieldErrors.bankAccountName ? ( +

{fieldErrors.bankAccountName}

+ ) : null} + + + setForm({ ...form, bankAccountNo: e.target.value.replace(/[^\d]/g, '') })} + /> + {fieldErrors.bankAccountNo ? ( +

{fieldErrors.bankAccountNo}

+ ) : null} + + + setForm({ ...form, bankBranch: e.target.value })} + /> + +
+ + {editing ? ( + + ) : null} +
+
+
+ ) : ( +

仅主账号可维护收款账户

+ )} + + {msg ?

{msg}

: null} +
+
+ ); +} diff --git a/apps/h5-shop/src/pages/MinePage.tsx b/apps/h5-shop/src/pages/MinePage.tsx index 471cf12..304c8a9 100644 --- a/apps/h5-shop/src/pages/MinePage.tsx +++ b/apps/h5-shop/src/pages/MinePage.tsx @@ -153,6 +153,12 @@ export default function MinePage() { account_balance_wallet 结算提现 + {profile?.isPrimary ? ( + + ) : null} {profile?.isPrimary ? (