|
|
@@ -56,14 +56,14 @@
|
|
|
|
|
|
| 表 | 关键字段 | 用途 |
|
|
|
|---|---|---|
|
|
|
-| `users` | `id`, `referrerId`, `referralCode`, `nickname`, `avatar` | 用户与推荐关系 |
|
|
|
+| `users` | `id`, `referrerId`, `referralCode`, `nickname`, `avatar`, `created_at` | 用户与推荐关系 |
|
|
|
| `referral_tree` | `parentId`, `childId`, `level`, `path` | 物化全层级推荐树 |
|
|
|
-| `product_orders` | `buyerId`, `totalAmount`, `status`, `paidAt` | 消费订单 |
|
|
|
-| `commission_records` | `referrerId`, `buyerId`, `commissionAmount`, `level`, `status` | 佣金记录(L1/L2) |
|
|
|
-| `user_platform_balance` | `userId`, `totalEarned`, `available`, `frozen`, `withdrawn` | CF 值钱包 |
|
|
|
-| `cf_rate_tier` | `minTeamSize`, `ratePercent`, `enabled` | 返佣阶梯(现有) |
|
|
|
+| `product_orders` | `buyerId`, `totalAmount`, `status`, `fulfillStatus`, `paidAt` | 消费订单(fulfillStatus=completed 表示已完成履约) |
|
|
|
+| `commission_records` | `referrerId`, `buyerId`, `commissionAmount`, `level`, `status` | 佣金记录(L1/L2;status=settled/cancelled) |
|
|
|
+| `user_platform_balance` | `userId`, `totalEarned`, `available`, `frozen`, `withdrawn` | CF 值钱包(现有,本需求不直接读) |
|
|
|
+| `cf_rate_tier` | `minTeamSize`, `ratePercent`, `enabled` | 返佣阶梯(现有,复用其 maxLevel 配置) |
|
|
|
|
|
|
-> 注:`referral_tree.level` = 代际差(1=直推,2=间推...),`path` = `/最远祖先/.../直接推荐人/`
|
|
|
+> 注:`referral_tree.level` = 代际差(1=直推,2=间推...);`commission_records.level` = 1/2(仅 L1/L2 结算佣金);订单完成以 `fulfillStatus = 'completed'` 为准。
|
|
|
|
|
|
### 3.2 后端接口设计
|
|
|
|
|
|
@@ -93,8 +93,8 @@
|
|
|
```
|
|
|
|
|
|
**实现逻辑:**
|
|
|
-- `memberConsumption`:查 `product_orders` 表 `buyerId = 该成员` 且 `status IN ('completed', 'paid', 'received')` 的 `totalAmount` 总和
|
|
|
-- `myCfEarnedFromMember`:查 `commission_records` 表 `referrerId = 我` 且 `buyerId = 该成员` 且 `status != 'cancelled'` 的 `commissionAmount` 总和
|
|
|
+- `memberConsumption`:查 `product_orders` 表 `buyerId = 该成员` 且 `fulfillStatus = 'completed'` 的 `totalAmount` 总和(仅统计已完成履约的订单)
|
|
|
+- `myCfEarnedFromMember`:查 `commission_records` 表 `referrerId = 我` 且 `buyerId = 该成员` 且 `status != 'cancelled'` 的 `commissionAmount` 总和(含 settled + pending)
|
|
|
|
|
|
#### 3.2.2 新增 `/api/commission/team/consumption`
|
|
|
|
|
|
@@ -117,7 +117,7 @@
|
|
|
**实现逻辑:**
|
|
|
1. 查 `referral_tree` 表 `parentId = 我` 且 `level <= maxLevel` 的所有 `childId`
|
|
|
2. 按 `level` 分组统计 `memberCount`
|
|
|
-3. 查这些 `childId` 的 `product_orders`(completed 状态)`totalAmount` 总和,按 `level` 汇总
|
|
|
+3. 查这些 `childId` 的 `product_orders`(`fulfillStatus = 'completed'`)`totalAmount` 总和,按 `level` 汇总
|
|
|
|
|
|
**配置化:**
|
|
|
- 读取 `cf_rate_tier` 表中 `enabled=1` 的记录,或新增 `commission_config` 表存储 `team_consumption_max_level`(默认 3)
|
|
|
@@ -150,23 +150,16 @@
|
|
|
```json
|
|
|
{
|
|
|
"nodes": [
|
|
|
- { "userId": 100, "nickname": "我", "level": 0, "parentId": null, "referrerId": 456 },
|
|
|
- { "userId": 101, "nickname": "直推A", "level": 1, "parentId": 100, "referrerId": 100 },
|
|
|
- { "userId": 102, "nickname": "间推B", "level": 2, "parentId": 101, "referrerId": 101 },
|
|
|
- { "userId": 103, "nickname": "直推C", "level": 1, "parentId": 100, "referrerId": 100 }
|
|
|
+ { "userId": 101, "nickname": "直推A", "avatar": "https://...", "level": 1, "parentId": 100, "referrerId": 100, "memberConsumption": 32000, "myCfEarnedFromMember": 9600 },
|
|
|
+ { "userId": 102, "nickname": "间推B", "avatar": "https://...", "level": 2, "parentId": 101, "referrerId": 101, "memberConsumption": 8000, "myCfEarnedFromMember": 2400 },
|
|
|
+ { "userId": 103, "nickname": "直推C", "avatar": "https://...", "level": 1, "parentId": 100, "referrerId": 100, "memberConsumption": 15000, "myCfEarnedFromMember": 4500 }
|
|
|
],
|
|
|
"totalMembers": 3,
|
|
|
"maxLevel": 3
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-**实现逻辑:**
|
|
|
-1. 查 `referral_tree` `parentId = 我` 且 `level <= maxLevel` → 后代集合 `{childId, level}`
|
|
|
-2. 对每个 `childId`,查 `users` 表得 `nickname`, `avatar`, `referrerId`
|
|
|
-3. `referrerId` 即为树中的直接父节点(`referrerId = 我` 则父节点为我)
|
|
|
-4. 组装扁平数组,前端递归渲染树形
|
|
|
-
|
|
|
-> 也可复用 `DistributionService.getTeamTree` 逻辑,但该服务基于 `distribution_relations` 表,当前推荐体系基于 `users.referrerId` + `referral_tree`,建议在 `CommissionService` 新增实现。
|
|
|
+> **说明:** `nodes` 数组**不包含根节点("我")**——仅包含 team 成员(level >= 1);根节点仅用于前端统计/上下文。每个节点已包含 `memberConsumption`(该成员自身已完成履约订单总额)和 `myCfEarnedFromMember`(我从该成员消费获得的 CF 总额)。前端按 `parentId` 递归构建树,`level` 用于层级标签与缩进。
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -182,25 +175,25 @@
|
|
|
|
|
|
**树形 UI 实现思路(从下往上生长):**
|
|
|
```vue
|
|
|
-<!-- 树形容器:flex column-reverse,根节点在底部 -->
|
|
|
+<!-- 树形容器:flex column-reverse,L1节点在底部,分支向上延伸 -->
|
|
|
<view class="tree-container" style="flex-direction: column-reverse;">
|
|
|
- <!-- 递归组件 TreeNode -->
|
|
|
- <TreeNode :node="rootNode" :level="0" :maxLevel="maxLevel" />
|
|
|
+ <!-- L1节点作为根:扁平 nodes 先按 parentId 组装为树,再渲染 -->
|
|
|
+ <TreeNode v-for="root in level1Roots" :key="root.userId" :node="root" :level="1" :maxLevel="maxLevel" />
|
|
|
</view>
|
|
|
|
|
|
<!-- TreeNode.vue -->
|
|
|
-<view class="tree-node" :style="{ 'padding-left': level * 30 + 'rpx' }">
|
|
|
+<view class="tree-node" :style="{ 'padding-left': (level - 1) * 30 + 'rpx' }">
|
|
|
<view class="node-main" @click="toggle">
|
|
|
<image :src="node.avatar" />
|
|
|
<text>{{ node.nickname }}</text>
|
|
|
- <tag v-if="node.level > 0">L{{ node.level }}</tag>
|
|
|
- <text class="consumption">消费: ¥{{ node.memberConsumption/100 }}</text>
|
|
|
- <text class="my-cf">我得CF: ¥{{ node.myCfEarnedFromMember/100 }}</text>
|
|
|
+ <tag>L{{ node.level }}</tag>
|
|
|
+ <text class="consumption">消费: ¥{{ (node.memberConsumption/100).toFixed(2) }}</text>
|
|
|
+ <text class="my-cf">我得CF: ¥{{ (node.myCfEarnedFromMember/100).toFixed(2) }}</text>
|
|
|
<icon :name="expanded ? 'up' : 'down'" />
|
|
|
</view>
|
|
|
<!-- 子节点:因为父容器 column-reverse,子节点会在上方渲染 -->
|
|
|
<view v-if="expanded && node.children" class="children">
|
|
|
- <TreeNode v-for="child in node.children" :key="child.userId" :node="child" :level="level+1" />
|
|
|
+ <TreeNode v-for="child in node.children" :key="child.userId" :node="child" :level="level+1" :maxLevel="maxLevel" />
|
|
|
</view>
|
|
|
<!-- 连接线:::before 画竖线,父节点中心向上 -->
|
|
|
</view>
|
|
|
@@ -275,7 +268,7 @@ public Result<Map<String, Object>> teamTree(@RequestAttribute("userId") Long use
|
|
|
| 团队成员无消费订单 | 消费额显示 0,CF 获得显示 0 |
|
|
|
| `maxLevel` 超过实际层级 | 按实际最大层级统计 |
|
|
|
| `referral_tree` 数据不一致 | 以 `users.referrerId` 为准,`referral_tree` 为辅助加速查询 |
|
|
|
-| 订单状态:仅统计 `completed`/`paid`/`received` | 排除 `cancelled`/`refunding`/`refunded` |
|
|
|
+| 订单状态:仅统计已完成履约 | 高 | 严格筛选 `fulfillStatus = 'completed'`(排除 pending/confirmed_by_vendor/received_by_user) |
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -328,7 +321,7 @@ public Result<Map<String, Object>> teamTree(@RequestAttribute("userId") Long use
|
|
|
| 风险 | 等级 | 对策 |
|
|
|
|---|---|---|
|
|
|
| `referral_tree` 数据量大,树形查询慢 | 中 | 限制 `maxLevel`≤5,加索引 `idx_parent_level(parentId, level)` |
|
|
|
-| 消费额统计包含退款订单 | 高 | 严格筛选 `status` 仅 completed/paid/received |
|
|
|
+| 消费额统计包含退款订单 | 高 | 严格筛选 `fulfillStatus = 'completed'`(仅统计已完成履约的订单,排除未结算/退货状态) |
|
|
|
| 树形 UI 在小程序渲染卡顿 | 低 | 虚拟列表/分页加载,层级>3 懒加载 |
|
|
|
| 后台配置未生效(缓存) | 低 | 配置读取走 Redis,更新后清理 |
|
|
|
|