674b6ac31b
Co-authored-by: Cursor <cursoragent@cursor.com>
5.7 KiB
5.7 KiB
杜康好客 · v4.0.20 开发文档
2026-09-16 / 09-17 · promo / city-scope / store / admin-web / h5-partner / shared-types
主题:推广码渠道负责人(多选主合伙人)+ 关联合伙人(扫码 first-lock)
2026-09-17 修订:HQ 渠道负责人 / 关联合伙人下拉与表格展示为「公司(如有)-人名」
1. 版本目标
| # | 任务 | 类型 | 交付 |
|---|---|---|---|
| 1 | 渠道负责人 | 需求 | HQ 创建/编辑推广码可多选主合伙人;被指定主账号在合伙人 H5 只看扫码人数、归因人数、订单数量 |
| 2 | 关联合伙人 | 需求 | HQ 可单选绑定主合伙人;登录用户扫该码且尚未关联任何人时 first-lock;已关联他人静默跳过;不回刷历史 |
不做:把推广码改成关联码(仍用数字 scene,不复用 pa_ / sa_);回刷历史归因用户;已关联他人换绑;合伙人 H5 开放用户/订单/核销/手机号明细;历史 ownerUserId(C 端用户)迁成合伙人;子账号推广码数据入口。
2. 规则
规则事实源:杜康好客-v4-PRD.md §2.1。
flowchart TD
scan[C端扫推广码] --> touch["POST /promo/touch"]
touch --> scanInc[scanCount++ 未登录也计]
touch --> login{已登录?}
login -->|否| done[结束]
login -->|是| attr[首次归因 UserPromoAttribution]
attr --> src[ORGANIC 才标 PROMO_CODE 来源]
src --> hasAssoc{码上有关联合伙人?}
hasAssoc -->|否| done
hasAssoc -->|是| bound{用户已有 assoc?}
bound -->|无| lock["PartnerCityService.tryBindIfUnbound"]
bound -->|已是同一人| skipSame[noop]
bound -->|已是他人| skipOther[静默跳过]
lock --> users[合伙人H5 关联用户可见]
- 渠道负责人:可空、多选 ACTIVE 主合伙人(
isPrimary=1)。只授权看三项汇总,不绑用户。 - 关联合伙人:可空、单选 ACTIVE 主合伙人。登录 touch 后
tryBindIfUnbound:无关联则写assoc_partner_account_id+assoc_bound_at(不写assoc_sub_account_id、不改sourceType、不增加关联码assoc_scan_count);已是同一人 noop;已是他人不抛错。只绑以后扫进来的用户。 - 两字段独立:只配渠道负责人 ≠ 绑用户;只配关联合伙人也会把用户写入该合伙人「关联用户」,并允许看三项汇总。
- HQ 原
ownerUserId列保留、创建/编辑不再暴露。 - 展示:渠道负责人、关联合伙人(筛选/创建/编辑下拉、列表列、详情)统一为「公司-人名」;无公司则只显示人名。多人用顿号拼接。
3. API
3.1 HQ(promo 模块)
POST /admin/promo-codes、PUT /admin/promo-codes/:id 增加:
channelOwnerPartnerIds: string[](可空)assocPartnerAccountId: string | null(可空;空串/null 解绑)
列表/详情返回 channelOwners[]、assocPartner(id / companyName / name / phone)。列表筛:channelOwnerPartnerId、assocPartnerAccountId。
3.2 C 端扫码
POST /promo/touch:登录后、归因与来源标记之后,若码上有 assocPartnerAccountId,调用 PartnerCityService.tryBindIfUnbound(不 import StoreModule,避免 Nest 循环依赖)。
3.3 合伙人 H5(仅主账号,PartnerPrimaryGuard)
| 方法 | 路径 | 返回 |
|---|---|---|
| GET | /partner/promo-codes |
{ items: [{ id, name, code, status, scanCount, attributionCount, orderCount }] } |
可见范围:当前主账号是渠道负责人 或 关联合伙人。订单数 = status=COMPLETED。禁止 HQ 的 users / metrics / timeline / 订单快链。
4. 变更面
| 层 | 路径 |
|---|---|
| Prisma | CommonPromoCode.assocPartnerAccountId;PromoCodeChannelOwner(promo_code_channel_owner) |
| 迁移 | server/dukang-api/prisma/migrate-promo-partner-fields.sql(Review 后生产执行) |
| shared-types | promo.ts:PromoCodePartnerBrief、PromoCodeItem.channelOwners/assocPartner、PartnerPromoCodeItem |
| API promo | promo-code.service.ts、dto/promo-code.dto.ts、partner-promo-code.controller.ts;PromoModule import CityScopeModule |
| API city-scope | PartnerCityService.tryBindIfUnbound |
| API store | PartnerAssocService.tryBindIfUnbound 转调 city-scope |
| admin-web | PromoCodesPage.tsx、promo/PromoCodeDetailPage.tsx:渠道负责人多选 / 关联合伙人单选;去掉关联用户 ID;展示「公司-人名」(lib/promo-partner-label.ts) |
| h5-partner | CenterPage.tsx 运营管理「推广码数据」;/center/promo-codes;PromoCodesPage.tsx |
| 本地代理 | admin/partner/shop Vite 默认 VITE_API_TARGET → http://127.0.0.1:3010(Windows 上 localhost 可能打到占用 ::3010 的其他进程) |
5. 验收
- HQ 创建/编辑推广码可多选渠道负责人、可清空单选关联合伙人;下拉与列表/详情展示「公司-人名」(无公司则人名);可按两字段筛选
- 只配渠道负责人:扫码不写用户关联;该主账号 H5「推广码数据」能看到三项数字,点不开用户/订单/核销
- 只配关联合伙人:未关联的登录用户扫码后出现在该合伙人「关联用户」;该主账号也能看三项汇总
- 用户已关联他人:扫带关联合伙人的码不换绑、流程不中断、不报「无法更换」
- 为已有码补关联合伙人:不回刷历史归因用户
- 子账号中心无「推广码数据」入口
- 关联码
pa_/sa_行为不变;已付佣金快照不回刷 - 执行迁移后 API 可启动(
PromoModule不得再 importStoreModule) - shared-types 构建通过;相关 lint 过