Files
dukang/docs/杜康好客-v4.0.18-开发文档.md
T

11 KiB
Raw Blame History

杜康好客 · 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 财务「全部银行账户」目录(聚合门店结算资质/酒厂/合伙人/物流 + 不挂门店的「其他」账户)
2026-09-08 再修订:撤销门店多收款账户(store_bank_account);打款与财务门店行均读门店详情「结算资质」主账号 bank_account_*


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 与门店端/HQ「收款账户」维护页。一门店一行,取绑定主账号(is_primary=1)的 bank_account_name / bank_account_no / bank_branch;无主账号则取最早绑定账号。
  • 对应 HQ 门店详情页签「结算资质」:结算户名、银行账号、开户银行。
  • 打款/结算/提现/账单导出统一读上述字段(loadStoreDefaultBank),实时查库,改后无需重启。
  • 门店端不再提供收款账户入口;银行信息由总部在结算资质维护。

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_*)。菜单名:全部银行账户。

有效账户(同时满足才入列):结算户名、银行账号均非空;来源状态为有效。

类型 数据源 有效条件 本页可写
门店 门店详情结算资质(主账号 StoreAccount.bank_account_*) 户名+账号已填;一店一行 仅备注
酒厂 system_config WINERY_BANK_* 户名+账号已填 仅备注
合伙人 partner_account status=ACTIVE、主账号、户名+账号已填 仅备注
物流 common_fulfillment_provider status=ACTIVE、户名+账号已填(仓无银行字段) 仅备注
其他 finance_bank_account status=ACTIVE 增删改
  • 列表:类型、归属(快链)、城市、结算户名、银行账号、开户银行、备注。归属快链:门店→详情「结算资质」;酒厂→系统设置「酒厂银行账户」;合伙人→城市合伙人详情;物流→仓配管理并打开该承运商;其他→本页编辑弹窗。
  • 新增不关联门店 = 类型「其他」,不进入提现/账单打款/酒厂/物流对账。
  • 来源账户字段仍在原处改;本页只读这些字段。备注:其他写本表;来源写 overlay 表 finance_bank_account_note(门店 sourceId = storeId)。
  • 筛选:类型、城市、关键字(户名/账号/开户银行/归属名/备注)。导出按当前筛选全量 Excel / PDF。
  • 权限:HQ finance。

3. API

3.1 关联码(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.2 财务全部银行账户(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:{storeId} / PARTNER:{partnerId} / LOGISTICS:{providerId} / WINERY:winery / OTHER:{id}。bankAccountNo 校验 ^\d{8,32}$(「其他」入参)。


4. 变更面

层 路径
Prisma schema.prisma(FinanceBankAccount、FinanceBankAccountNote;User.assocSubAccountId;已删除 StoreBankAccount)
迁移 migrate-user-assoc-sub-account-v4018.sql、migrate-finance-bank-account-v4018.sql、migrate-drop-store-bank-account-v4018.sql(历史 migrate-store-bank-account-v4018.sql 仅作曾上线回填)
domain store-address.ts(formatStoreDisplayAddress:省+市+区+详细地址原样拼接,不去重)
shared-types settlement.ts(FinanceBankAccountDto);hq-list-columns.ts(finance-bank-accounts);partner-assoc.ts(summary/touch/bind 增 subAccountId/isSubAccount)
API store store-bank.util.ts(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 已删除收款账户页;MinePage.tsx 去掉入口
admin-web StoresPage.tsx 结算资质;BankAccountsPage.tsx(财务全部银行账户);已删除收款账户页签
h5-partner AssocQrcodePage.tsx、UsersManagePage.tsx(子账号提示)

5. 验收

  • C 端门店列表地址 = 省+市+区+详细地址原文;详细地址已含省市区时仍重复拼接(例:金水东路333号 → 河南省郑州市金水区金水东路333号;详细地址写全称 → 河南省郑州市金水区河南省郑州市金水区金水东路333号);空值回退「地址待完善」;搜索「省/市/区县」关键词可命中
  • 门店无独立收款账户页;打款/提现/账单导出读结算资质(结算户名、银行账号、开户银行);改后无需重启即时生效
  • 子账号可生成并下载自己的二维码(scene sa_{subId}),与主账号 pa_{id} 不冲突
  • 扫子账号码:用户仍锁定主账号(佣金归主账号),并写入 assoc_sub_account_id
  • 子账号用户管理/关联码页展示自己维度的已扫码/已关联/订单统计;主账号聚合 own+children 不变
  • 主账号码扫码:assoc_sub_account_id 为空,行为与 v4.0.9 一致
  • HQ 财务菜单「全部银行账户」:门店行来自结算资质(一店一行)+ 有效酒厂/主合伙人/承运商账户;可新增不挂门店账户;类型/城市/关键字筛选;Excel 与 PDF 导出与筛选一致;备注可写;结算资质改后刷新即更新
  • shared-types 构建通过;相关 lint/单测过