front/docs/渠道代理与返佣设计方案.md
zzh c66071ed5e
All checks were successful
Build and Deploy Front / deploy (push) Successful in 6m43s
feat: 渠道代理门户与运营列表,并修复分区明细工具栏误删分区
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 18:38:18 +08:00

535 lines
22 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 渠道代理与返佣设计方案
> **文档状态**:产品 + 概念模型 + 主流程 + 表结构草案(**轻量首版已收口,可开发**)
> **编写日期**:2026-10-29(同日按 §14 收窄为首版默认)
> **关联系统**:OMS 业务(`docs/oms系统设计-050801.md`)、RBAC(`docs/rbac系统设计-060801.md`)、数据权限(`docs/数据权限设计.md`)、客户裂变(`docs/客户裂变与营销体系设计方案.md`)
> **关联规范**:`docs/前端页面设计规范.md`
> **目标读者**:产品、运营、研发、测试
---
## 0 阅读指引
| 角色 | 优先阅读 |
| --- | --- |
| 产品 / 运营 | §1~§4、§6、§10 |
| 前端 | §5、§7、§9 |
| 后端 / DBA | §3~§8 |
| 测试 | §4、§6、§10、§11 |
**分期对照(正文已按轻量默认)**
| 阶段 | 内容 | 交付 |
| --- | --- | --- |
| **首版(P0+P1 一次交付)** | 开户、注册绑定、订单快照、返佣明细、运营列表、薄代理门户 | 能归属、能出应结清单、代理只读;**无冻结入账、无提现、无毛利基数** |
| **P2+** | 毛利基数、多行产品规则、冻结/余额、提现、自动 clawback | 账户化与精细规则 |
> 代理 **不碰价格**;结算 **只做返佣明细 + 运营线下打款**。详细裁剪理由见 §14。
---
## 1 背景与目标
### 1.1 业务背景
OMS 当前是 **单运营方部署**(非多租户 SaaS):客户挂 `t_customer`,订单挂 `CustomerId`,组织树只管内部员工。已有能力中与「代理」最接近的是:
| 现有能力 | 说明 | 为何不够 |
| --- | --- | --- |
| C2C 邀请(`inviteCode` → `ReferrerCustomerId`) | 客户邀客户,推荐佣金 | 受益人是客户,不是渠道代理 |
| 员工顾问(`?advisor=` → `CustomerService`) | 内部客服软绑定 | 不是外部渠道方,无独立结算主体 |
| 价档 `PriceTierKey` | 客户卖价分档 | 代理方案 **明确不改价** |
需要单独建设 **渠道代理**:通过专属注册链接获客,只看名下客户与订单,按返佣结算,且 **不引入 SaaS 多租户**。
### 1.2 建设目标
1. **获客归因**:代理专属注册链接,客户注册时绑定 `AgentId`。
2. **数据可见**:代理仅能看名下客户及其订单(只读;不看渠道成本/应付)。
3. **返佣结算**:按规则计提返佣 **明细**;首版运营按清单线下打款(不做冻结入账/提现)。
4. **身份清晰**:代理是第三种业务主体(与员工、客户并列),独立登录入口。
### 1.3 设计原则
1. **不走 SaaS**:同一库、同一运营方;用 `AgentId` 做归属,不引入 `Fake.MultiTenant` / 按代理拆库。
2. **代理不碰价格**:查价 / 下单 / 卖价仍走现有销售产品与 `PriceTierKey`;不做代理加价册。
3. **结算只做返佣**:客户仍与平台钱包结算;代理只拿佣金。
4. **注册绑定 + 下单快照**:客户长期挂 `AgentId`;订单落库时快照 `Order.AgentId`,事后换绑不影响历史单。
5. **与 C2C 邀请拆开**:`AgentId` 与 `ReferrerCustomerId` 分字段、分账本,避免双发佣金。
6. **组织树不参与**:代理不挂 `organization`;数据范围靠 `AgentId`,不靠 DataPerm 组织。
---
## 2 已确认业务规则
| # | 决策 | 落点 |
| --- | --- | --- |
| 1 | **不做多租户 / 不做代理改价** | 无代理价册;无租户连接串 |
| 2 | **结算模式 = 可配置返佣** | 首版基数仅 **运费 / 应收**;比例 + 可选销售产品列表挂在代理上(见 §5.4);**毛利为 P2+** |
| 3 | **绑定时机 = 注册** | 链接带 `agent` 参数;登录后不靠链接改绑 |
| 4 | **订单归属 = 快照** | `Order.AgentId` = 下单时 **主客户** 的 `AgentId` |
| 5 | **登录入口 = 代理门户** | **不从运营端登录**;运营端只做开户与规则配置 |
| 6 | **登录凭证 = 代理编号** | 账号为 `AgentCode` + 密码;`loginType=2` |
| 7 | **RBAC 定位** | 新增 `loginType=2` + 业务表 `t_agent` + 固定角色 `Agent`;权限码宜少(约 3 个) |
| 8 | **组织** | 代理不进组织树 |
| 9 | **与邀请互斥(首版)** | 注册请求带 `agent` 时只走代理归因,不写 `ReferrerCustomerId`、不发 C2C 推荐佣金 |
| 10 | **可见费用** | 代理可见客户侧应收/实付与返佣金额;**不可见** `payable` / 买价 / 毛利 |
| 11 | **换绑** | 首版不做自助换绑;运营改 **主客户** `AgentId`(仅影响之后新单) |
| 12 | **子客户归属** | **跟主客户**:快照前 `ResolveMainCustomer` |
| 13 | **计提触发** | **`deliverySuccess` / `deliveryFailed`** 均计提;同单按 `OrderId` 幂等 |
| 14 | **首版资金形态** | **只写返佣明细,不冻结、不入余额、不提现**;运营按清单线下打款;错账人工改明细(P2+ 再账户化) |
---
## 3 定位:相对用户 / 角色 / 权限 / 组织
### 3.1 一句话
**代理 = 第三种业务主体**(并列于内部员工、客户)。
登录壳子用 RBAC `user`;业务身份在 `t_agent`;功能门禁用角色权限;数据范围用 `AgentId`。**组织不参与。**
### 3.2 三种主体对照
| | 内部员工 | 客户 | **渠道代理** |
| --- | --- | --- | --- |
| `loginType` | `0` | `1` | **`2`(新增)** |
| RBAC `user` | 有 | 有(桥接) | **有(桥接)** |
| 业务主表 | 无 | `t_customer` | **`t_agent`** |
| 角色 | 各类内部角色 | `Customer` / `CustomerSub_*` | **固定角色 `Agent`** |
| 组织 `OrganizationId` | 可挂 | **不挂** | **不挂** |
| 数据范围 | 组织 / DataPerm | 自己的 `CustomerId` | **自己的 `AgentId`** |
| 登录入口 | 运营端 | 客户门户 / H5 | **代理门户**(独立) |
| 登录账号 | 员工 Account | 手机/邮箱/客户编号 | **`AgentCode`(代理编号)** |
| 核心诉求 | 履约与运营 | 下单支付 | 获客可见 + 返佣 |
### 3.3 分层关系
```
┌─ RBAC:谁登录、能点什么功能 ─────────────────────────┐
│ user ──user_role──► role ──► role_permission / menu │
│ └── OrganizationId(仅员工) │
└────────────────────────────────────────────────────────┘
│ mapId / UserId
▼
┌─ OMS 业务身份:你是哪种人、数据归谁 ──────────────────┐
│ 员工:靠组织 / DataPerm │
│ 客户:t_customer(下单、余额) │
│ 代理:t_agent(获客归属、返佣) │
│ └── Customer.AgentId → Order.AgentId(快照) │
└────────────────────────────────────────────────────────┘
```
| 概念 | 管什么 | 代理怎么用 |
| --- | --- | --- |
| **用户 User** | 账号、Token、`mapId` | 开户时建 `user`,`t_agent.UserId` 指向它 |
| **角色 Role** | 权限码集合载体 | 预置 `Agent`,开通时绑定 |
| **权限 Permission** | 菜单 / API 门禁 | 代理门户只读与佣金相关码 |
| **组织 Organization** | 内部部门树 | **代理不进**;运营在后台「代理管理」菜单维护名单 |
### 3.4 明确不做的混用
| 错误做法 | 原因 |
| --- | --- |
| 给员工加「代理」角色进运营端 | 混全量数据与运营能力;返佣主体不清 |
| 用客户账号兼代理 | 与自身下单、余额、C2C 邀请搅在一起 |
| 把代理挂进组织树当「下级公司」 | 组织不是渠道容器;会逼近伪 SaaS |
---
## 4 登录入口
### 4.1 结论
**代理不从运营端登录。**
| 入口 | 受众 | 代理 |
| --- | --- | --- |
| 运营端 / 管理后台 | 内部员工 `loginType=0` | ❌ 不登录;仅被运营管理 |
| 客户门户 / H5 | 发货客户 `loginType=1` | ❌ |
| **代理门户** | 渠道代理 `loginType=2` | ✅ |
### 4.2 门户形态(实现可选,语义固定)
- 同前端工程:登录 `/agent`,门户 `/agent/customers`、`/agent/orders`
- 或子域:`agent.<域名>`
登录成功后只加载代理菜单,不出现运营菜单、不出现客户下单主流程(除非后续明确要「代客下单」,首版不做)。
### 4.3 运营侧职责(仍用员工账号)
运营在后台完成:代理开户 / 启停、返佣三字段(基数类型 / 比例 / 可选销售产品)、人工改绑主客户 `AgentId`、查看 / 导出返佣明细并线下打款。
这是 **「管代理」**,不是代理本人会话。
---
## 5 主流程:注册绑定 → 下单 → 结算
### 5.1 总览
```
运营开通代理 → 生成注册码 / 链接
→ 客户经链接注册 → 写 Customer.AgentId
→ 客户正常查价 / 下单 / 支付(价格逻辑不变)
→ 订单快照 Order.AgentId(跟主客户)
→ 派送成功/失败 → 写一条返佣明细(金额快照)
→ 运营按明细线下打款
→(P2+)冻结入账 / 提现 / 毛利基数 / 自动 clawback
```
### 5.2 注册绑定
**链接示例**
- PC:`/register?agent=AG001`
- H5 / 微信:同等 query;与现有 `inviteCode`、`advisor` 并列解析
**注册时(在现有创建客户流程旁路归因)**
1. 解析 `agent` 码 → 查 `t_agent` 且状态为启用。
2. 写入 `Customer.AgentId`、`AgentBindTime`(不写 Channel 类扩展字段)。
3. 创建客户、RBAC 用户、价档等 **与今天一致**;代理不参与报价。
4. 若同时带了 `inviteCode`:**首版以 `agent` 为准**,不写 `ReferrerCustomerId`。
**约束**
- 只认注册时绑定;登录后带链接不改绑。
- 无码注册 → `AgentId` 为空。
- 已存在客户补绑:首版仅运营后台人工改。
### 5.3 下单快照
客户查价、下单、充值支付:**业务路径不因代理而改价**。
| 场景 | 归属 |
| --- | --- |
| 主客户已绑定代理 | 解析主客户 → 取其 `AgentId` 写入订单快照 |
| 子客户下单 | **跟主客户**:`Order.AgentId = ResolveMainCustomer(下单客户).AgentId`(与营销主客户解析对齐) |
| 事后运营改主客户 `AgentId` | **仅影响新单**;历史订单快照不变 |
| 主客户未绑定 | `Order.AgentId` 为空,不返佣 |
快照伪代码:
```
mainCustomer = ResolveMarketingMainCustomer(orderCustomer)
Order.AgentId = mainCustomer.AgentId // 允许为空
```
### 5.4 返佣结算(首版轻量)
**触发(均计算提成)**
| 订单状态 | 是否计提 |
| --- | --- |
| `deliverySuccess`(派送成功) | ✅ |
| `deliveryFailed`(派送失败) | ✅ |
| 其他状态 | ❌ |
同一订单只生成 **一条** 明细(幂等:按 `OrderId` 判重);已有明细则跳过。失败↔成功来回 **不自动重算、不自动扣回**,错了运营改明细。
**公式**
```
返佣金额 = 基数金额(BaseAmount) × 返佣比例(CommissionRate)
```
**规则挂在 `t_agent` 上(不建独立规则表)**
| 字段 | 说明 |
| --- | --- |
| `BaseType` | 首版仅 `freight`(运费)/ `receivable`(应收) |
| `CommissionRate` | 比例,如 `0.0300` |
| `SalesProductIds` | 可选;空 = 该代理全部产品都计提;非空 = 仅订单 **主销售产品** 命中列表才计提 |
| `BaseType` | 含义 | 取数示意 |
| --- | --- | --- |
| `freight` | 运费 | 对齐 `t_order_fee` 约定运费项(实现时定字段) |
| `receivable` | 应收 | 客户侧应收合计(或约定应收口径) |
| `grossProfit` | 毛利 | **首版不做**(P2+);应收 − 应付,仅服务端算 |
**其他约定**
- 写明细时 **快照** `BaseType`、`BaseAmount`、`CommissionRate`、主销售产品;改代理规则不追溯旧单。
- `BaseAmount ≤ 0`、无 `Order.AgentId`、或产品未命中列表 → 不生成明细。
- **不做** 冻结天数、定时入账、自动 clawback。
**明细状态(首版两个就够)**
| 状态 | 含义 |
| --- | --- |
| `pending` | 已计提,待运营线下结算 |
| `paid` | 运营已标记打款(可选;也可用备注/导出勾选,不做复杂工作流) |
**资金边界**
- 客户:继续与平台 `Balance` 结算(不变)。
- 代理首版:**无佣金钱包、无提现**;应结金额以明细为准,运营线下打款。
- P2+:再引入余额 / 冻结 / 提现 / 毛利 / 多行规则表。
### 5.5 代理可见范围
| 数据 | 可见 | 不可见 |
| --- | --- | --- |
| 名下客户列表 / 基础资料 | ✅ | 可按需脱敏手机号 |
| 名下订单列表 / 轨迹 / 状态 | ✅(`Order.AgentId = 我`) | 他代理数据 |
| 客户侧费用(应收 / 实付) | ✅ | `payable`、买价、毛利 |
| 返佣明细 | ✅ | — |
| 改价、改渠道、审充值、仓操作 | ❌ | — |
列表查询 **必须** 服务端强制 `AgentId = 当前代理`。
---
## 6 与现有邀请佣金的关系
| | C2C 邀请(已有) | 渠道代理(本方案) |
| --- | --- | --- |
| 注册参数 | `inviteCode` | `agent` |
| 客户字段 | `ReferrerCustomerId` | `AgentId` |
| 受益主体 | 另一个客户 | `t_agent` |
| 账本 | `t_referral_commission_*` + 客户佣金余额 | 首版仅 `t_agent_commission_detail`(无代理余额) |
| 价格 | 不碰 | 不碰 |
| 同时存在 | 首版互斥(有 `agent` 只走代理) | 同左 |
可复用:状态触发挂钩、规则参数快照、按单幂等。首版 **不抄** 推荐佣金的冻结入账与会员门槛。
**不要** 把代理塞进 `ReferrerCustomerId`。
---
## 7 数据模型草案
> 命名与审计字段遵循 OMS 惯例:`IsDeleted`、`CreateUserId`、`CreateTime`、`UpdateUserId`、`UpdateTime`。
> **禁止** 新增库级 `UNIQUE KEY`(业务唯一性在应用层校验)。编码类字段可建普通索引。
### 7.1 `t_agent` — 代理主数据(含返佣三字段)
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `Id` | INT PK | |
| `UserId` | INT | 对应 RBAC `user.userid` / JWT `mapId` |
| `AgentCode` | VARCHAR(32) | **登录账号 + 注册链接编码**;应用层保证启用态唯一 |
| `AgentName` | VARCHAR(64) | 显示名 |
| `Phone` / `Email` | VARCHAR | 联系用(非登录主键) |
| `Password` | VARCHAR | 登录密码(对齐客户模式) |
| `Status` | 枚举 | 启用 / 停用 |
| `BaseType` | VARCHAR(32) | `freight` / `receivable`(首版) |
| `CommissionRate` | DECIMAL | 返佣比例 |
| `SalesProductIds` | VARCHAR/TEXT 可空 | 可选产品 Id 列表(JSON 或逗号);空=不限产品 |
| `Remark` | VARCHAR | |
> **首版不建** `t_agent_commission_rule`、**不建** `FreezeDays` / `CommissionBalance`。按产品不同比例、毛利、余额 → P2+ 再扩。
### 7.2 `t_customer` 扩展
| 字段 | 说明 |
| --- | --- |
| `AgentId` | INT 可空;绑定代理(建议写在主客户上) |
| `AgentBindTime` | DATETIME 可空 |
### 7.3 `t_order` 扩展
| 字段 | 说明 |
| --- | --- |
| `AgentId` | INT 可空;**下单快照**,返佣只认此字段 |
### 7.4 `t_agent_commission_detail` — 返佣明细
| 字段 | 说明 |
| --- | --- |
| `AgentId` / `OrderId` / `CustomerId` | 归属 |
| `SalesProductId` | 订单主销售产品(快照,可空) |
| `BaseType` | 基数类型快照 |
| `BaseAmount` | 计提基数(快照) |
| `CommissionRate` | 比例(快照) |
| `CommissionAmount` | 金额 |
| `Status` | `pending` / `paid`(首版) |
| `TriggerOrderStatus` | `deliverySuccess` / `deliveryFailed` |
| `PaidTime` / `Remark` | 可选;运营标记已打款 |
### 7.5 P2+ 再议(首版不做)
- `t_agent_commission_rule`(多行产品规则)
- `CommissionBalance` / 冻结入账 / `t_agent_commission_withdraw`
- `BaseType=grossProfit`、自动 clawback
### 7.6 RBAC / 认证扩展
| 项 | 说明 |
| --- | --- |
| `OmsLoginType.Agent = 2` | Claims `loginType=2` |
| 角色 `Code=Agent` | 开通时代入 `user_role` |
| 权限码(宜少) | `Agent.Portal`、`Agent.Customers.Query`、`Agent.Orders.Query`(佣金可并进订单页则可不单列) |
| 运营权限 | `Agent.Admin.Query`、`Agent.Admin.Create`、`Agent.Admin.Update`、`Agent.Admin.Rebind` |
---
## 8 接口与模块边界(草案)
### 8.1 运营端(员工)
| 能力 | 建议路径前缀 |
| --- | --- |
| 代理 CRUD / 启停 / 比例 | `/agent/admin/*` |
| 客户改绑 Agent | `/agent/admin/rebind` |
| 返佣明细查询 / 标记已打款 | `/agent/admin/commission/*` |
### 8.2 认证
| 能力 | 说明 |
| --- | --- |
| 代理登录 | 扩展现有 `AuthService`:账号 = **`AgentCode`** + 密码,签发 `loginType=2` |
| 刷新令牌 | 与现有一致,按 `loginType` 分支 |
### 8.3 代理门户
| 能力 | 建议路径前缀 |
| --- | --- |
| 名下客户分页 | `/agent/portal/customers/*` |
| 名下订单分页 / 详情(脱敏成本字段) | `/agent/portal/orders/*` |
| 返佣明细(可与订单同页) | `/agent/portal/commission/*` 或并入 orders |
### 8.4 注册归因改动点
- PC / H5 / 微信注册入参增加 `agent`(或 `agentCode`)。
- `CustomerService.CreateAsync`(及微信注册同源逻辑)增加 `AgentBinder`,与 `MarketingReferralBinder`、`StaffAdvisorBinder` 并列。
- 下单创建订单处:`ResolveMainCustomer` → 写 `Order.AgentId`。
- 订单状态机在 **`deliverySuccess` 与 `deliveryFailed`** 两处挂钩计提(幂等写明细)。
---
## 9 前端页面范围
### 9.1 运营端
- 代理列表 / 新建编辑 / 启停(路由 `/agentAdmin/list`)
- 返佣明细(路由 `/agentAdmin/commission`)
- (可选)客户详情展示所属代理、改绑入口
遵循 `docs/前端页面设计规范.md`(容器、工具栏、TablePro、权限码)。
**菜单管理(须同步)**
1. 菜单管理新建一级菜单:名称「渠道代理」,路由 `/agentAdmin`,类型目录/菜单。
2. 在该一级行点「同步子菜单及按钮」→ 写入「代理管理」「代理返佣」及按钮权限。
3. 把权限赋给运营角色后刷新即可在侧栏看到。
### 9.2 代理门户(首版三页即可)
- 登录页 `/agent`(`AgentCode` + 密码)
- 客户列表 `/agent/customers`(只读)
- 订单列表 + 返佣明细 `/agent/orders`
样式可复用中后台组件,**路由与布局与运营端隔离**。
> **不要**把代理门户页面同步进运营端菜单管理;代理端独立登录,不走后台菜单树。
### 9.3 注册页
- 识别 `agent` 参数;提交注册时带回。
- 无需向客户展示复杂代理信息(可选展示「渠道推荐」文案)。
---
## 10 分期与验收
### 首版(一次交付)
- [ ] `t_agent`(含 `BaseType` / `CommissionRate` / `SalesProductIds`)+ 客户/订单 `AgentId`
- [ ] 运营开户、启停、改三字段规则、改绑主客户
- [ ] 注册链接归因;有 `agent` 时不写 C2C 引荐
- [ ] 下单快照跟主客户;`deliverySuccess` / `deliveryFailed` 幂等写明细
- [ ] 运营返佣明细列表 / 导出 / 可选标记 `paid`
- [ ] `loginType=2`,`AgentCode` 登录 + 角色 `Agent`
- [ ] 代理门户三页只读 + 接口强制 `AgentId`
- [ ] **无** 冻结、余额入账、提现、毛利基数、自动 clawback
### P2+
- [ ] `grossProfit` 基数
- [ ] 多行产品规则表
- [ ] 冻结 / `CommissionBalance` / 提现
- [ ] 自动 clawback
- [ ] 改绑审计强化
---
## 11 测试要点
1. 无码注册 → 无 `AgentId`、无返佣。
2. 带码注册 → 绑定正确;停用代理无法绑定。
3. `agent` + `inviteCode` 同传 → 只走代理。
4. 子客户下单 → 快照 = 主客户 `AgentId`。
5. 改主客户 `AgentId` → 旧单不变。
6. 代理 A 查不到代理 B 数据。
7. 代理接口无应付/买价/毛利字段。
8. 成功与失败均可出明细;同单不重复。
9. `SalesProductIds` 非空且主产品未命中 → 不发佣;改规则不追溯旧明细。
10. `freight` / `receivable` 取数与账单口径一致。
11. `AgentCode` 登录门户;进不了运营端。
12. 无提现、无自动入余额;运营可标记 `paid` 或仅导出打款。
---
## 12 风险与边界
| 风险 | 应对 |
| --- | --- |
| 与裂变佣金双发 | 注册互斥;账本分离 |
| 运营端误授代理权限 | 独立 `loginType` + 独立门户 |
| 误做成多租户 | 方案明确禁止拆库/租户解析 |
| 代理要改价 | 本方案明确拒绝;若未来需要加价,另立方案,不在本文范围 |
| 派送失败仍发佣 | 已确认;`BaseAmount≤0` 不发;已有明细不自动追 |
| 改费后金额不准 | 快照不重算;运营改明细 |
---
## 13 评审结论(含轻量收口)
| # | 议题 | 结论 |
| --- | --- | --- |
| 1 | 返佣基数 | 业务可配运费/应收/毛利;**首版只做运费/应收**,毛利 P2+ |
| 2 | 计提触发 | `deliverySuccess` 与 `deliveryFailed` 均计提 |
| 3 | 子客户归属 | 跟主客户 |
| 4 | 登录凭证 | `AgentCode` + 密码 |
| 5 | 提现 / 余额 | **首版都不做**;明细 + 线下打款 |
| 6 | 规则形态 | **挂在 `t_agent` 三字段**,不建规则表 |
| 7 | clawback | 首版不做自动扣回 |
**实现前仍需对齐的字段映射**
1. `freight` / `receivable` 对应 `t_order_fee` 的具体费用项。
2. 状态码以 `OrderStatusCodes.DeliverySuccess` / `DeliveryFailed` 为准。
3. 产品限制按订单 **主销售产品** 匹配 `SalesProductIds`。
然后出 DDL(`sql/migrate_agent_module.sql`)与接口明细即可开发。
---
## 14 轻量原则与坑点(已采纳为正文默认)
> §0~§10 已按本节收口;此处保留备忘,避免开发时加回复杂度。
### 14.1 首版最薄闭环
```
开户(AgentCode + BaseType/Rate/产品列表)
→ 注册绑定 → 下单快照(跟主客户)
→ 派送成功/失败写一条明细
→ 运营导出线下打款
→ 代理门户只读
```
### 14.2 明确推迟到 P2+
冻结入账、`CommissionBalance`、提现、`grossProfit`、多行规则表、自动 clawback。
### 14.3 必防坑
| 坑 | 对策 |
| --- | --- |
| 费用事后变更 | 明细快照;不自动重算 |
| 失败后退款 | `BaseAmount≤0` 不发;已发靠人工 |
| 状态来回 | 按 `OrderId` 幂等 |
| 子客户无 AgentId | 必须 `ResolveMainCustomer` |
| C2C 双发 | 有 `agent` 只走代理 |
| 门户串权 | 查询强制 `AgentId` |
| `loginType=2` 漏改 | 对照客户登录分支清单 |
| 只挂成功态 | 失败态也要挂钩 |