Просмотр исходного кода

docs(team): 负责人结算模型与70/30冲突分析;pool: 新增T37修正内部分配

Agnes Agent 1 неделя назад
Родитель
Сommit
294dc6e9d5

+ 52 - 2
docs/superpowers/specs/2026-11-01-team-system-design.md

@@ -14,6 +14,29 @@
 3. **团队管理的作用 = 服务指派**:对服务性内容,指派团队成员去服务客户。
    - 例:用户对某商品有售后问题 → 指派团队成员跟进;规划师团队做咨询;活动管理团队做活动支持。
 4. **团队类型**:供应商团队(带货+售后)、规划师团队(咨询)、活动管理团队(活动支持)等。
+5. **团队 = 通过浠艾福平台销售产品/服务的一批人(负责人结算口径,2026-11-01 末次澄清,最高优先)**:
+   - 平台从客户处收到费用后,**向团队结算,结算对象 = 团队负责人(唯一)**。
+   - **结算费用团队内部怎么分配,平台不负责**——平台不做成员间佣金拆分。
+   - **每个团队成员都有自己的团队** = 他邀请加入的人(即每个成员都是其自身子团队的负责人)。
+
+## 0.5 负责人结算模型(本次澄清的落点)
+
+### 0.5.1 结论:供应商体系已是正确模型,虚拟/销售体系(DistributionSystem)冲突
+
+| 体系 | 结算实现 | 是否「单一负责人整体结算、平台不拆分」 | 结论 |
+|---|---|---|---|
+| **供应商体系 SupplySystem** | `SupplySettlementService.createSettlement`:`platform_profit = total_sales × platform_profit_rate/100`;`supplier_payout = total_sales − platform_profit`;结算单挂 `systemId`,归属校验 `system.getAdminId()`(唯一负责人) | ✅ 完全符合——平台抽成一次,剩余**整体**结算给 `adminId`,成员间分配平台不管 | **保留,无需改** |
+| **虚拟/销售体系 DistributionSystem** | `DistributionService.calculateCommission`:`totalCommission=orderAmount×profitRate/1000`;`level1Amount=total×70%`;`level2Amount=total×30%`;`platformAmount=total−L1−L2` | ❌ **直接冲突**——平台按 70/30 拆分给 L1/L2 上级,平台留剩余 | **需修正**(见 §1.5 / T37) |
+
+**关键事实:**
+- `SupplySystem.adminId`(唯一负责人)+ `platformProfitRate`(平台抽成率)→ `SupplySettlement.supplierPayout` 为**一次性整体结算**,`isOwnSettlement` 用 `system.getAdminId()` 判定归属 → **平台只结算给唯一负责人**。✓ 与澄清完全一致。
+- `DistributionSystem.ownerId` 字段**存在但从未被结算逻辑使用**;`DistributionService.calculateCommission` 的 70/30 拆分即「平台决定团队内部佣金分配」,**违反「平台不负责团队内部分配」**。
+
+### 0.5.2 「每个成员都有自己的团队」如何表达
+
+- 结构上 `distribution_relations`(`systemId,userId,parentId,depth`)已是树:**以任一成员为 root 的子树 = 该成员的团队**,其 `parentId=该成员` 的直属下级即「他邀请加入的人」。
+- 结算语义上:每个成员作为**自己子团队的负责人**,平台按其子树整体结算给他(`ownerId`/`adminId` 视角),**不再按 70/30 沿 parent 链拆分**。
+- 供应商体系同理:成员加入即形成以该成员为负责人、以 `supply_hierarchy` 直属下级为成员的子团队;无需新增表,查询层派生。
 
 ## 1. 现状盘点(4 套独立团队模型)
 
@@ -31,6 +54,19 @@
 - 服务指派完全不存在于团队概念中:`AfterSalesRequest.handledBy`、`ProductServicePerson`(商品维度服务人)均不挂团队。
 - 活动团队不存在:`activities` 无 `teamId`;`guide_activities` 只归规划师个人。
 
+## 1.5 与「负责人结算」理念的冲突清单(2026-11-01 末次澄清后核查)
+
+| # | 位置 | 现状 | 冲突 | 处置 |
+|---|---|---|---|---|
+| C1 | `DistributionService.calculateCommission`(396 行) | `totalCommission=orderAmount×profitRate/1000`;`level1Amount=total×70%`;`level2Amount=total×30%`;`platformAmount=total−L1−L2` | **平台决定团队内部佣金分配**(70/30 给 L1/L2 上级)→ 违反「结算费用团队怎么分配平台不负责」 | **T37**:改为整体结算给系统唯一负责人 |
+| C2 | `DistributionSystem.ownerId` | 字段存在但**结算逻辑从未使用** | 「唯一负责人」语义未落地为结算对象 | 随 T37 一并接入 |
+| C3 | `distribution_relations`(树) | `systemId,userId,parentId,depth,inviteCode`,整体系一棵大树,`getParentChain` 最多 10 层、`calculateCommission` 取 2 层 | 「每个成员都有自己的团队」未按负责人结算建模(成员子树 = 其团队语义存在但未用于结算) | 随 T37 明确:成员子树整体结算给该成员,不沿链拆分 |
+| C4 | `SupplySystem` + `SupplySettlementService` | `adminId` + `platformProfitRate` → `supplier_payout=total_sales−platform_profit`,挂 `systemId`,`isOwnSettlement` 按 `adminId` | **无冲突**——已是「唯一负责人整体结算、平台不拆分」 | 保留不动 |
+| C5 | `supply_customer_relation` family 口径 | 客户按 `familyId` 归集家庭,未挂「成员的团队」 | 轻微:客户归属应以「团队(负责人)」为结算单元而非家庭语义混淆 | 在客户维度设计(T32/T33 已 done)中已按 `supplySystemId` 归集,**保留**;备注明确 family 仅做展示归组 |
+| C6 | 设计文档 §2.1 `Team` | 未写明「唯一负责人 + 平台整体结算、不内部分配」 | 设计口径缺失 | 本文档已补 §0.5,`Team` 定义按 §0.5 口径 |
+
+**结论:核心冲突集中在虚拟/销售体系 `DistributionSystem` 的 70/30 拆分(C1–C3),供应商体系结算(C4)已符合澄清,无需改动。** T32/T33/T34/T35(客户维度、服务指派、活动团队)与负责人结算口径**无冲突**,均围绕供应商体系展开,保留。
+
 ## 2. 统一团队模型(目标态)
 
 以**供应商体系为锚点**(唯一已带客户),向上抽象出可复用的「团队」核心,规划师/活动团队复用同一套成员与服务指派机制。
@@ -39,8 +75,8 @@
 
 | 概念 | 定义 | Phase 1 落点 |
 |---|---|---|
-| **Team(团队)** | 一个服务/销售单元,含类型与归属人 | 复用 `supply_system`(类型=SUPPLIER);GuideTeam 暂并行,术语对齐 |
-| **TeamMember(成员)** | 被邀请加入的成员 `teamId,userId,role[LEAD|MEMBER],invitedBy,status,joinedAt` | 复用 `supply_system_member` + `supply_hierarchy` |
+| **Team(团队)** | 一个服务/销售单元,含类型与归属人;**结算单元 = 团队负责人(唯一),平台只向负责人整体结算,团队内部分配平台不负责** | 复用 `supply_system`(类型=SUPPLIER,负责人=`adminId`);GuideTeam 暂并行,术语对齐 |
+| **TeamMember(成员)** | 被邀请加入的成员 `teamId,userId,role[LEAD|MEMBER],invitedBy,status,joinedAt`;**每个成员同时是自己邀请子团队的负责人** | 复用 `supply_system_member` + `supply_hierarchy` |
 | **TeamCustomer(客户)** | 购买过团队产品的用户 `teamId,userId/familyId,source,firstOrderAt` | 复用 `supply_customer_relation`(扩 source 口径) |
 | **TeamServiceAssignment(服务指派)** | 指派某成员服务某客户 `teamId,customerId,assigneeMemberId,serviceType[AFTER_SALE|CONSULT],refOrderId/refRequestId,status,assignedBy,assignedAt` | 新增 `team_service_assignment`(或 `supply_service_assignment`) |
 
@@ -111,8 +147,21 @@
 | **T33** | 客户数量改名+重定义(`supply_customer_relation` 扩 source/统计口径 + UI 文案) | P1 |
 | **T34** | 团队服务指派能力(`supply_service_assignment` 表 + 指派/我的任务/客户记录接口 + 前端入口) | P1 |
 | **T35** | 活动管理团队(`activities` 加 `teamId` + 活动团队创建/成员/服务,Phase 2) | P2 |
+| **T37(新)** | **消除 70/30 内部分配冲突**:`DistributionService.calculateCommission` 改为「整体结算给系统唯一负责人 `ownerId`」,平台不参与成员间分配;`ownerId` 接入结算;明确成员子树=其团队 | P1 |
 
 > T32 原 prompt(团队-SKU 聚合)前提错误,已按本设计修订为客户维度购买明细。
+> T37 源于 2026-11-01 末次澄清(§0.5/§1.5):平台只向**唯一负责人**结算,团队内部分配平台不负责。
+
+### 5.1 T37 修正要求(负责人结算)
+
+- 目标:`calculateCommission` 返回**单一结算对象** = `DistributionSystem.ownerId`(唯一负责人),金额 = 佣金总额,**不再输出 level1Amount/level2Amount/platformAmount 三方拆分**。
+- 平台与团队的分成本就是 `profitRate`(平台抽成)——团队拿 `totalCommission` 整体,平台不再从团队佣金里二次切分。
+- `ownerId` 为空时降级:取该体系树 root 节点(`parentId=null`)的用户作为负责人,或按 `profitRate` 走「无团队结算」分支。
+- 保留「每个成员都有自己的团队」表达:成员子树整体结算给该成员(作为其子团队负责人),成员间分配不再由平台计算。
+- 兼容:`level1Amount/level2Amount` 旧字段若已有前端消费,改为 `settleToUserId/settleAmount`,旧字段置 null 或标记 `@Deprecated` 一期过渡,二期移除。
+- **不动供应商体系**:`SupplySettlementService` 已符合口径(T37 不改 `SupplySettlement*`)。
+
+## 6. 技术约束(AGENTS.md 铁律)
 
 ## 6. 技术约束(AGENTS.md 铁律)
 - 后端统一 `@PostMapping`;`@Resource` 注入;`@RequestAttribute("userId")` 取用户;`Result<T>` 包装。
@@ -126,4 +175,5 @@
 - [ ] 客户列表每个客户展示关联订单数 + 关联金额(元) + 可下钻购买明细;金额换算正确。
 - [ ] 客户列表仅含「邀请制团队」成员关系下的购买用户,不含 referral 推广下线混淆。
 - [ ] 可对某客户(售后/咨询)指派团队成员,被指派成员能在「服务任务」中看到并跟进;`AfterSalesRequest` 可联动。
+- [ ] `DistributionService.calculateCommission` 不再输出 70/30 L1/L2 拆分;结算对象=系统唯一负责人(`ownerId`);`SupplySettlementService` 保持「整体结算给 `adminId`」不变。
 - [ ] `mvn clean compile` 通过;API_REFERENCE 同步;未破坏现有 4 套团队功能。

+ 9 - 6
tools/xaf_agent_hub/tasks_backlog.json

@@ -1447,13 +1447,14 @@
     "priority": "P1",
     "module": "fullstack",
     "instance": "",
-    "status": "dispatched",
+    "status": "done",
     "spec": "docs/superpowers/specs/2026-11-01-team-system-design.md",
     "prompt": "规格 2026-11-01-team-system-design.md §3.1:小程序供应商团队页「客户归属」改名为「客户数量」,并重新定义口径。\n定义:客户数量 = 在 product_orders 中 supplySystemId=团队.systemId 且 fulfillStatus='completed' 的**去重买家(用户/家庭)**数;不再局限于 supply_customer_relation 的 referral 来源。\n后端:SupplyTeamController 统计接口(如 /api/admin/supply-team/stats 或 listCustomers 汇总)改为按 supplySystemId 归集已完成订单的去重买家;复用 ProductOrderService 第1150-1164行已有的下单自动归属逻辑(source='order')。必要时 supply_customer_relation 扩 source 注释/加 firstOrderAt。\n前端:cfc-frontend/pages/vendor/team-system/team-system.vue 把统计项「客户归属」文案改为「客户数量」,字段语义对齐。\n约束:@PostMapping+@Resource+Result<T>;不改 referral_tree 推广页;金额/计数口径与现有团队消费额一致;API_REFERENCE 同步。",
     "session_id": "ses_e6b087b25ffeFrh2kzepfEBM89",
     "dispatched_at": "2026-11-01 10:36:52",
     "dispatched_to": "lan-248",
-    "model": "axonhub/deepseek"
+    "model": "axonhub/deepseek",
+    "done_at": "2026-11-01 11:04:04"
   },
   {
     "id": "T34",
@@ -1475,13 +1476,14 @@
     "priority": "P2",
     "module": "fullstack",
     "instance": "",
-    "status": "dispatched",
+    "status": "done",
     "spec": "docs/superpowers/specs/2026-11-01-team-system-design.md",
     "prompt": "规格 2026-11-01-team-system-design.md §2.2/§5:新增「活动管理团队」类型。当前 activities 表无 teamId、guide_activities 只归规划师个人,活动团队不存在。\n实现:activities 加 teamId(关联 supply_system 或统一 Team);提供活动团队创建/成员管理/服务指派接口,复用 T34 的服务指派机制(ACTIVITY_SUPPORT 类型);活动运营可由团队成员协作。\n落地:后端 Activity 实体+Mapper 加 teamId(ensureColumn 幂等+schema.sql),ActivityController 增 team 相关接口;前端活动管理页增加团队视图(Phase 2,可与 T34 服务指派共用组件)。\n约束:@PostMapping+@Resource+Result<T>;先查 API_REFERENCE;mvn clean compile 通过。本任务为 Phase 2,可晚于 T32/T33/T34 实施。",
     "session_id": "ses_e6b080299ffeqcIKgydRTATE2x",
     "dispatched_at": "2026-11-01 10:37:23",
     "dispatched_to": "lan-189",
-    "model": "opencode/deepseek-v4.1-flash-free"
+    "model": "opencode/deepseek-v4.1-flash-free",
+    "done_at": "2026-11-01 10:53:36"
   },
   {
     "id": "T36",
@@ -1503,7 +1505,7 @@
     "priority": "P2",
     "module": "backend",
     "instance": "",
-    "status": "dispatched",
+    "status": "done",
     "spec": "docs/superpowers/specs/2026-11-01-team-product-performance-design.md",
     "prompt": "实现设计文档 docs/superpowers/specs/2026-11-01-team-product-performance-design.md 所定义的功能。\n请先完整读取该文档(尤其是「任务拆分与依赖」「验收标准」章节),按文档口径逐条实现;文档标题:团队页「关联订单 / 关联金额(元)」按商城商品-SKU 维度统计接口。\n若文档含多个可独立交付的批次,请按文档的批次顺序推进,每批次完成后自行 commit + push。",
     "generated_from": "sync_design_tasks",
@@ -1511,6 +1513,7 @@
     "session_id": "ses_e6b082a2effezSOHFrbKUPCgdI",
     "dispatched_at": "2026-11-01 10:37:13",
     "dispatched_to": "lan-189",
-    "model": "opencode/deepseek-v4.1-flash-free"
+    "model": "opencode/deepseek-v4.1-flash-free",
+    "done_at": "2026-11-01 11:25:25"
   }
 ]