12 KiB
杜康好客 · v4.0.18 开发文档
2026-09-07 · mini-user / store / settlement / iam / h5-shop / admin-web / h5-partner / shared-types / domain
主题:C 端门店列表省市区县地址;门店多收款账户;子账号独立继承二维码;HQ 财务银行账户总目录
2026-09-08 修订:门店列表地址按「省+市+区+详细地址」原样拼接,详细地址已含省市区时不去重
2026-09-08 追加:HQ 财务「银行账户」总目录(聚合门店/酒厂/合伙人/物流 + 不挂门店的「其他」账户)
1. 版本目标
| # | 任务 | 类型 | 交付 |
|---|---|---|---|
| 1 | C 端门店列表地址 | 需求 | 「省 + 市 + 区 + 详细地址」原样拼接展示(不去重);搜索匹配完整地址 |
| 2 | 门店多银行账号 | 需求 | 门店级收款账户列表;设「默认」账户用于打款;切换无需重启 |
| 3 | 子账号二维码 | 需求 | 子账号独立继承码 sa_{subId};新增一级子账号维度统计 |
| 4 | HQ 财务银行账户总目录 | 需求 | 财务菜单下列出有效账户;可新增不挂门店的「其他」账户;类型/备注/筛选/Excel+PDF 导出 |
不做:银行账号历史打款回刷;子账号佣金独立归属(佣金仍归主账号);按承运商维度的收款账户;「其他」账户接入打款;本页改写门店/合伙人/酒厂/物流源字段。
2. 规则
2.1 门店列表地址
C 端门店列表卡片「地址」一栏(门店详情页同规则)= 下拉省 + 下拉市 + 下拉区 + 详细地址 input 原文,四段按字段原样拼接,中间不加分隔符。
| 字段 | 来源 | 存库 |
|---|---|---|
| 省 | 录店/改店省市区下拉 | store.province |
| 市 | 同上 | store.cityName |
| 区 | 同上 | store.district |
| 详细地址 | 用户自由输入(地图选点若回填也写入此字段) | store.address |
禁止去重:不得因详细地址已包含省/市/区而省略前缀。助手 formatStoreDisplayAddress(packages/domain)/ fullStoreAddress(mini-user)只做 trim 后拼接;空则回退「地址待完善」。
| 下拉省市区 | 详细地址 | 列表展示 |
|---|---|---|
| 河南省 / 郑州市 / 金水区 | 金水东路333号 | 河南省郑州市金水区金水东路333号 |
| 河南省 / 郑州市 / 金水区 | 河南省郑州市金水区金水东路333号 | 河南省郑州市金水区河南省郑州市金水区金水东路333号 |
搜索关键字同时匹配 address / district / 完整拼接结果。规则实现:formatStoreDisplayAddress(domain,后端地理编码同用)与 mini-user fullStoreAddress(列表/详情展示)。
2.2 门店多银行账号
- 表
store_bank_account挂在storeId:一门店多账户;is_default=1为打款默认账户(每店至多一个)。 - 迁移时按现有主账号银行字段(
StoreAccount.bank_account_name/no/branch)为每店回填一条默认账户。 - 打款/结算/提现导出统一读取默认账户(无默认则取第一个 ACTIVE;再退回旧主账号字段,兼容未迁移数据)。
- 切换/新增/删除账户不需要重启服务器:全仓无进程内缓存,
StoreAccount/PartnerAccount/FulfillmentProvider银行字段与酒厂system_config WINERY_BANK_*均每次请求实时查库。唯一「改后需重启」的是微信支付商户号WX_MCH_ID(system-config.registry.ts,requiresRestart: true),属微信支付、非银行账号。 - 权限:门店主账号可维护本店账户(子账号只读);总部可维护任意门店账户。
2.3 子账号继承二维码
flowchart LR
scanC[C端扫码 sa_subId] --> touch[touchScan 子账号 assocScanCount+1]
scanC --> bind[bindUser]
bind -->|first-lock| setAssoc[user.assocPartnerAccountId=主账号, assocSubAccountId=子账号]
setAssoc --> main[主账号聚合统计: 关联用户/订单]
setAssoc --> sub[子账号统计: 已扫码/已关联/订单]
- 复用
partner_account.assoc_qrcode_id / assoc_qrcode_resource_id / assoc_scan_count:子账号行存自己的sa_{subId}码(assoc_qrcode_id为@unique,sa_与pa_前缀、id 均不同,不冲突)。 - 新增
user_user.assoc_sub_account_id:记录带来该用户的子账号(主账号码扫码则为空)。 touchScan:sa_场景对子账号行assocScanCount自增;主账号已扫码在读取时聚合 own+children。bindUser:sa_场景解析子账号 → 主账号;first-lock 仍校验主账号,写入assocPartnerAccountId=主账号+assocSubAccountId=子账号。getSummary/getStats/listUsers/listAssocOrders:子账号调用时返回自己维度数据(assocSubAccountId=子账号);activityPosterId恒为空;主账号保持聚合行为不变。- 佣金仍归主账号,不因扫码来源为子账号而改变归属。
2.4 HQ 财务银行账户总目录
财务查阅与手工登记目录,不改打款主数据。打款仍读各业务源表(门店默认账户、合伙人主账号字段、承运商字段、酒厂 WINERY_BANK_*)。
有效账户(同时满足才入列):户名、银行账号均非空;来源状态为有效。
| 类型 | 数据源 | 有效条件 | 本页可写 |
|---|---|---|---|
| 门店 | store_bank_account |
status=ACTIVE;所有门店的全部有效账户(含非默认) |
仅备注 |
| 酒厂 | system_config WINERY_BANK_* |
户名+账号已填 | 仅备注 |
| 合伙人 | partner_account |
status=ACTIVE、主账号、户名+账号已填 |
仅备注 |
| 物流 | common_fulfillment_provider |
status=ACTIVE、户名+账号已填(仓无银行字段) |
仅备注 |
| 其他 | finance_bank_account |
status=ACTIVE |
增删改 |
- 列表:类型、归属、城市、户名、银行账号、开户行、备注;门店另显示是否默认。
- 新增不关联门店 = 类型「其他」,不进入提现/账单打款/酒厂/物流对账。
- 来源账户户名/账号/开户行仍在原处改;本页只读这些字段。备注:其他写本表;来源写 overlay 表
finance_bank_account_note。 - 筛选:类型、城市、关键字(户名/账号/开户行/归属名/备注)。导出按当前筛选全量 Excel / PDF。
- 权限:HQ
finance。
3. API
3.1 门店收款账户(store 模块)
| 方法 | 路径 | Guard | 说明 |
|---|---|---|---|
| GET | /shop/store/bank-accounts |
门店 | 本店账户列表 |
| POST | /shop/store/bank-accounts |
门店主账号 | 新增账户(首条自动默认) |
| PUT | /shop/store/bank-accounts/:id |
门店主账号 | 编辑账户 |
| DELETE | /shop/store/bank-accounts/:id |
门店主账号 | 删除(默认账户不可删) |
| POST | /shop/store/bank-accounts/:id/default |
门店主账号 | 设为默认 |
| GET | /admin/stores/:storeId/bank-accounts |
HQ | 指定门店账户列表 |
POST / PUT / DELETE / POST :id/default |
/admin/stores/:storeId/bank-accounts |
HQ | 总部维护 |
入参:{ bankAccountName, bankAccountNo, bankBranch? };bankAccountNo 校验 ^\d{8,32}$。
3.2 关联码(store 模块)
POST /user/partner-assoc/touch:scene 支持pa_{id}与sa_{subId};sa_对子账号行计数,返回{ partnerId, subAccountId, scanCounted, scanCount }。POST /user/partner-assoc/bind:scene 支持sa_;返回{ bound, alreadyBound, partnerId, subAccountId, partnerName }。GET /partner/assoc(summary):子账号返回{ partnerId, primaryAccountId, isSubAccount, qrcodeUrl, userCount, scanCount }。GET /partner/assoc/stats、GET /partner/assoc/users、GET /partner/assoc/orders:子账号返回自己维度。GET /partner/assoc/qrcode:子账号下载自己的码。
3.3 财务银行账户总目录(settlement 模块)
| 方法 | 路径 | Guard | 说明 |
|---|---|---|---|
| GET | /admin/finance/bank-accounts |
HQ finance |
聚合列表;type / cityId / keyword / page / pageSize |
| GET | /admin/finance/bank-accounts/export |
HQ finance |
format=xlsx|pdf,同筛选全量 |
| POST | /admin/finance/bank-accounts |
HQ finance |
新增「其他」 |
| PUT | /admin/finance/bank-accounts/other/:id |
HQ finance |
编辑「其他」 |
| DELETE | /admin/finance/bank-accounts/other/:id |
HQ finance |
删除「其他」 |
| PUT | /admin/finance/bank-accounts/:id/remark |
HQ finance |
任意类型写备注;id 为复合键 |
行 id:STORE:{storeBankAccountId} / PARTNER:{partnerId} / LOGISTICS:{providerId} / WINERY:winery / OTHER:{id}。bankAccountNo 校验 ^\d{8,32}$。
4. 变更面
| 层 | 路径 |
|---|---|
| Prisma | schema.prisma(新增 StoreBankAccount、FinanceBankAccount、FinanceBankAccountNote;User.assocSubAccountId) |
| 迁移 | migrate-store-bank-account-v4018.sql、migrate-user-assoc-sub-account-v4018.sql、migrate-finance-bank-account-v4018.sql |
| domain | store-address.ts(formatStoreDisplayAddress:省+市+区+详细地址原样拼接,不去重) |
| shared-types | settlement.ts(StoreBankAccountDto、FinanceBankAccountDto);hq-list-columns.ts(finance-bank-accounts);partner-assoc.ts(summary/touch/bind 增 subAccountId/isSubAccount) |
| API store | store-bank.service.ts、store-bank.controller.ts(新增);store.module.ts;store-bank.util.ts(loadStoreBankAccounts/loadStoreDefaultBank);partner-assoc.service.ts(sa_ 解析/生成/touch/bind/统计);store.service.ts(地理编码地址与 C 端同规则拼接) |
| API settlement | settlement.service.ts(summary/withdraw/admin-withdrawal/export 改读默认账户);finance-bank-account.service.ts、finance-bank-export.util.ts(财务银行账户总目录) |
| API ops | admin-redeem.service.ts(默认账户) |
| mini-user | stores/index.tsx、store-detail/index.tsx、lib/store-display.ts(fullStoreAddress)、lib/promo.ts(放行 sa_) |
| h5-shop | BankAccountsPage.tsx(新增)、App.tsx、MinePage.tsx、styles.css |
| admin-web | StoreBankAccountsPanel.tsx(新增)、StoresPage.tsx(收款账户页签);BankAccountsPage.tsx(财务银行账户总目录) |
| h5-partner | AssocQrcodePage.tsx、UsersManagePage.tsx(子账号提示) |
5. 验收
- C 端门店列表地址 = 省+市+区+详细地址原文;详细地址已含省市区时仍重复拼接(例:金水东路333号 →
河南省郑州市金水区金水东路333号;详细地址写全称 →河南省郑州市金水区河南省郑州市金水区金水东路333号);空值回退「地址待完善」;搜索「省/市/区县」关键词可命中 - 门店可新增/编辑/删除/设默认收款账户;默认账户用于提现打款与账单导出
- 切换默认账户后(无需重启)提现 summary 与打款信息即时生效
- 子账号可生成并下载自己的二维码(scene
sa_{subId}),与主账号pa_{id}不冲突 - 扫子账号码:用户仍锁定主账号(佣金归主账号),并写入
assoc_sub_account_id - 子账号用户管理/关联码页展示自己维度的已扫码/已关联/订单统计;主账号聚合 own+children 不变
- 主账号码扫码:
assoc_sub_account_id为空,行为与 v4.0.9 一致 - HQ 财务菜单「银行账户」:列出全部有效门店账户(含非默认)+ 有效酒厂/主合伙人/承运商账户;可新增不挂门店账户;类型/城市/关键字筛选;Excel 与 PDF 导出与筛选一致;备注可写;来源账户改后刷新即更新
- shared-types 构建通过;相关 lint/单测过