674b6ac31b
Co-authored-by: Cursor <cursoragent@cursor.com>
101 lines
5.7 KiB
Markdown
101 lines
5.7 KiB
Markdown
# 杜康好客 · 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`](./杜康好客-v4-PRD.md) §2.1。
|
||
|
||
```mermaid
|
||
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` 不得再 import `StoreModule`)
|
||
- [ ] shared-types 构建通过;相关 lint 过
|