Files
huangjingfen/.cursor/plans/fix_issues_0325-1_f8488785.plan.md
2026-03-28 11:26:32 +08:00

328 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
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.
---
name: Fix Issues 0325-1
overview: 修复 UniApp 移动端前端5 个 UI 问题)和 PHP 后端7 个业务逻辑问题)共 12 个问题,涉及会员码图片、海报生成、账单筛选、导航、资产数据展示、佣金周期轮巡、积分奖励、分销等级升级和 e2e 验收测试。
todos:
- id: a1-member-code-image
content: 修复会员码页面:小程序 image 标签加 v-if 防护API 返回值兜底
status: completed
- id: a2-spread-poster
content: 修复分销海报downloadFilestoreImage 失败时 Promise 需 resolve/reject增加错误处理
status: completed
- id: a3-bill-remove-queue-refund
content: 账单明细页移除"公排退款"标签和筛选项
status: completed
- id: a4-queue-status-back-btn
content: 佣金状态页左上角增加返回按钮NavBar
status: completed
- id: a5-assets-data-and-back
content: 后端 HjfAssets 接口补充 total_points_earned 字段 + 资产页增加返回按钮
status: completed
- id: b1-commission-guard
content: 增加校验:推荐人自己必须有报单订单才能获得推荐返现佣金
status: completed
- id: b2-points-pre-upgrade
content: 升级为会员分销等级前,不给直推/伞下奖励积分
status: completed
- id: b3-points-by-qty
content: 积分奖励按订单中报单商品数量(而非订单数)发放
status: completed
- id: b4-umbrella-points
content: 修复伞下积分:创客无伞下积分,云店及以上才有伞下奖励积分,检查配置与级差逻辑
status: completed
- id: b5-upgrade-count
content: 修复创客升级getDirectQueueOrderCount 补充 refund_status 检查,核实任务配置
status: completed
- id: b6-cycle-race
content: 修复推荐返现循环并发竞态:用数据库锁或原子计数器序列化位次计算
status: completed
- id: b7-e2e-verify
content: e2e 测试验收分销员等级配置的奖励积分(直推/伞下)和等级升级任务是否正确
status: completed
isProject: false
---
# 修复 docs/issues-0325-1.md 中的问题
## 第一部分UniApp 移动端前端问题
所有前端文件位于 `pro_v3.5.1/view/uniapp/` 目录下。
---
### A1. 会员码页面图片报错 (pages/users/user_member_code/index.vue)
**问题反馈:** 前端页面报错 `Failed to load local image resource /pages/users/user_member_code/false`
**问题分析:** 小程序端 `<image :src="qrc">` 没有 `v-if` 防护。当 `activityCodeApi` 接口返回 `routineUrl: false`(布尔值)时,`this.qrc` 被赋值为 `false`,运行时将其字符串化为路径 `/pages/users/user_member_code/false`,导致图片加载 500 错误。
**代码定位:**
```17:18:pro_v3.5.1/view/uniapp/pages/users/user_member_code/index.vue
<!-- #ifdef MP -->
<image :src="qrc" class="qrcode"></image>
```
以及 API 回调处(约第 135 行):
```javascript
this.qrc = routineUrl; // routineUrl 可能为 false/null
```
**修复方案:**
- 在小程序的 `<image>` 标签上添加 `v-if="qrc"`(第 18 行),与 H5 分支保持一致
- 在 `activityCodeApi()` 中添加兜底:`this.qrc = routineUrl || ''`
---
### A2. 分销海报加载不出来 (pages/users/user_spread_code/index.vue)
**问题反馈:** 海报加载不出来
**问题分析:** `downloadFilestoreImage()`(约第 354 行)返回的 Promise 在下载失败时**既不 resolve 也不 reject**,只调用了 `Tips()`。这导致 `await` 永久挂起,阻塞了整个海报生成流程。
```363:366:pro_v3.5.1/view/uniapp/pages/users/user_spread_code/index.vue
fail: function() {
return that.$util.Tips({
title: ''
});
```
**修复方案:**
- 在 `downloadFilestoreImage` 的 `fail` 回调中添加 `reject()`(或 `resolve('')`),让 Promise 正常结束
- 在 `spreadMsg` 中,将下载后的图片路径传入 `userPosterCanvas` 前,增加空值/空字符串检查
- 可选:在海报生成循环外包 try/catch确保 `uni.hideLoading()` 始终执行
---
### A3. 账单明细页 — 移除"公排退款" (pages/users/user_bill/index.vue)
**问题反馈:** 去掉公排退款
**问题分析:** 导航栏有"公排退款"筛选标签,列表项中也显示"公排退款"标记。需要全部移除。
**修复方案:** 从模板中删除以下内容:
- 导航栏中 `queue_refund` 类型对应的 `<view>`(第 23-27 行)
- 列表项中的 `<text v-if="vo.type === 'queue_refund'" ...>公排退款</text>` 标记(第 47-50 行)
---
### A4. 佣金状态页 — 增加返回按钮 (pages/queue/status.vue)
**问题反馈:** 左上角增加返回按钮
**问题分析:** 页面顶部没有 NavBar 和返回按钮。
**修复方案:** 参照其他页面的已有模式(如 `user_spread_user/index.vue`
- 从 `@/components/NavBar.vue` 导入 `NavBar`(小程序条件编译)
- 在 `components` 中注册
- 在模板中添加 `<NavBar titleText="佣金状态" :iconColor="iconColor" :textColor="iconColor" showBack :isScrolling="isScrolling" />`,用 `<!-- #ifdef MP -->` 条件编译包裹
- 添加对应的 data 属性(`iconColor`、`isScrolling`)和滚动事件处理
---
### A5. 资产页 — 数据不显示 + 增加返回按钮 (pages/assets/index.vue)
**问题反馈:**
1. "累计获得积分"不显示数据,是否没读取到数据?
2. 左上角增加返回按钮
**问题 1 分析:** "累计获得积分"显示为 `NaN`。前端读取 `assetsInfo.total_points_earned`,但后端 `HjfAssets::overview` 接口未返回该字段。
- 前端计算属性(约第 148 行):`Number(this.assetsInfo.total_points_earned).toLocaleString()` => `NaN`
- 后端响应([HjfAssets.php](pro_v3.5.1/app/controller/api/v1/hjf/HjfAssets.php) 第 42-50 行):仅返回 `frozen_points`、`available_points` 等,缺少 `total_points_earned`
**修复方案(后端):** 在 `HjfAssets::overview()` 的响应中增加 `total_points_earned` 字段。其值应为 `frozen_points + available_points`(累计获得 = 当前待释放 + 已释放):
```php
'total_points_earned' => $frozenPoints + (int)($user['available_points'] ?? 0),
```
**问题 2** 左上角没有返回按钮(同 A4
**修复方案:** 按照 A4 相同的模式添加带 `showBack` 的 NavBar。
---
## 第二部分:后端业务逻辑问题
所有后端文件位于 `pro_v3.5.1/app/` 目录下。
---
### B1. 自己不报单,推荐没有返现佣金(测试问题 #1
**问题反馈:** "自己不报单(没有购买过报单商品的订单),推荐没有返现佣金的"
**问题分析:** [StoreOrderCreateServices.php](pro_v3.5.1/app/services/order/StoreOrderCreateServices.php)(约第 998-1028 行)中的佣金周期逻辑在**被推荐人**下单报单商品时计算佣金,但没有校验**推荐人自己**是否也购买过报单商品。按业务规则,推荐人自己必须先报单(购买过报单商品),推荐下级报单才能获得返现佣金。
**修复方案:** 在 `computeOrderProductTruePrice` 中计算周期佣金之前(约第 1004 行),增加校验:若推荐人(`$spread_uid`)自己没有任何已支付的 `is_queue_goods=1` 订单,则跳过佣金计算(`$oneBrokerage = 0`)。或者在 `Pay.php::compute()` 中写入 `one_brokerage` 前做此校验。
---
### B2. 升级成为会员分销等级前,不给奖励积分(测试问题 #2
**问题反馈:** "判断升级成为**会员分销等级**前,不给奖励积分(直推奖励积分、伞下奖励积分)"
**问题分析:** 在 [Pay.php](pro_v3.5.1/app/listener/order/Pay.php)(第 180-184 行)中,流程是:先检查升级、再发放积分。这意味着如果当前订单使推荐人从 grade 0 升级为创客,`reward()` 调用时已经看到了新等级,会在触发升级的同一笔订单上就发放**直推奖励积分**或**伞下奖励积分**。
根据 PRD直推奖励积分和伞下奖励积分均应从推荐人**已成为会员分销等级之后**的订单才开始发放。触发升级的那笔订单本身不应给新升级的用户发积分。
**修复方案:** 在升级检查执行前,记录各上级用户的"升级前等级"快照,然后在 `reward()` 中使用**升级前的等级**判断是否有资格获得直推/伞下奖励积分。具体方式:
- 在 Pay.php 中 `checkUserLevelFinish` 之前,快照相关用户的 `agent_level`
- 将升级前的等级信息传入 `reward()`,用于资格判断
- 或者在 `reward()` 中检查用户的 `agent_level` 是否在同一事务/时间窗口内被更新,若是则跳过
---
### B3. 积分应按订单中报单商品数量发放,而非按订单数(测试问题 #3
**问题反馈:** "目前积分奖励是按订单数量来给了需要调整为按报单商品数量比如一次性下一个订单含3个报单商品应该给1500积分而不是500"
**问题分析:** `PointsRewardServices::reward()` 每笔订单只调用一次,发放基于等级的固定积分(如创客 500 分)。若**一个订单内**包含 3 个报单商品(商品数量=3应发放 3x500=1500 分,而非 500 分。
**修复方案:**
- 修改 `reward()` 增加 `$qty` 参数(默认为 1表示该订单中报单商品的数量
- 在 `grantFrozenPoints` 中将 `$points * $qty` 计算实际积分
- 在 [Pay.php](pro_v3.5.1/app/listener/order/Pay.php)(第 184 行)中,从 `$orderInfo` 的购物车信息计算订单内报单商品数量(`is_queue_goods=1` 的商品 `cart_num` 之和),传入 `reward()`
- 在 [HjfOrderPayJob.php](pro_v3.5.1/app/jobs/hjf/HjfOrderPayJob.php) 中做同样的修改
---
### B4. 伞下奖励积分规则:创客无伞下积分,云店及以上才有(测试问题 #4
**问题反馈:** "创客级别的分销员,伞下下单没有**伞下奖励积分**,升级为云店级别分销员之后,只要级别低于自己的,伞下下单都应有**伞下奖励积分**"
**问题分析:** 按 PRD 各等级积分配置:
| 等级 | 直推奖励积分 | 伞下奖励积分 |
| --------------- | ------ | ------ |
| 创客 | 500 | 0 |
| 云店 | 800 | 300 |
| 服务中心原PRD名服务商 | 1000 | 200 |
| 合伙人原PRD名分公司 | 1300 | 300 |
> **注意:** 数据库中等级名称已调整,"服务商"改为"服务中心""分公司"改为"合伙人"。后续代码及配置中以数据库实际名称为准。
创客的伞下奖励积分为 **0**,这是**正确的设计**(非 Bug。问题焦点是**云店及以上**级别的会员在伞下下单后应获得伞下奖励积分,但目前可能未正确发放。
在 [PointsRewardServices.php](pro_v3.5.1/app/services/hjf/PointsRewardServices.php) 第 109 行,级差逻辑 `$actual = max(0, $reward - $lowerReward)` 可能导致云店的伞下积分被下级已获得的奖励抵消为 0。
**修复方案:**
- 核查 `eb_agent_level` 表中各等级的 `umbrella_reward_points` 是否与 PRD 一致(创客=0、云店=300、服务中心=200、合伙人=300
- 确认 `AgentLevelServices` 的 `getUmbrellaRewardPoints()` 和 `getDirectRewardPoints()` 正确读取该字段并返回
- 如果数据配置正确但云店仍未拿到伞下积分,检查级差计算中 `$lowerReward` 的传递链是否正确——特别注意当中间层(如创客)的伞下积分为 0 时,传给上级的 `$lowerReward` 应为 0云店的 `$actual` 应为 `max(0, 300 - 0) = 300`
- 若中间层是以**直推奖励积分**500作为 `$lowerReward` 传递的,则需修复 `propagateReward` 使其在递归中区分直推与伞下,确保向上传递的是**伞下**奖励而非直推奖励
---
### B5. 用户 ID=14 只有 2 单就升级为创客(测试问题 #5
**问题反馈:** "用户ID=14直推了2单就升级成为创客的分销等级了但是目前分销等级中创客的升级任务配置的是直推3单"
**问题分析:** [AgentLevelTaskServices.php](pro_v3.5.1/app/services/agent/AgentLevelTaskServices.php)(第 431 行)中 `getDirectQueueOrderCount()` 统计推荐人直推下级的报单订单数。可能原因:
- 统计包含了已退款的订单(查询条件有 `is_del=0`,但缺少 `refund_status` 检查)
- `no_assess=0` 过滤条件未正确生效
- `eb_agent_level_task` 表中创客的任务 `number` 被配置为 2 而非 3
- 竞态条件:并发订单处理可能触发两次升级检查
**修复方案:**
- 检查 `eb_agent_level_task` 表确认创客的任务配置为 `type=6`、`number=3`
- 在 `getDirectQueueOrderCount` 查询中补充 `refund_status` 条件检查(目前缺失,而其他任务类型均有 `refund_status => [0, 3]` 的检查)
- 在升级检查处增加日志,记录升级时刻的精确计数
---
### B6. 推荐返现循环两次都给了 20%(测试问题 #6
**问题反馈:** "**推荐返现循环**目前配置的是'[20,30,50]'。为何用户ID=14直推了2单出现了2次20%的返现佣金?"
**问题分析:** 在 [StoreOrderCreateServices.php](pro_v3.5.1/app/services/order/StoreOrderCreateServices.php) 第 1009-1015 行,位次计算通过统计推荐人下级所有已支付的报单订单数来确定。若两笔订单几乎同时处理,两次 `compute()` 调用可能看到相同的 `completedCount`,导致位次相同(都得到 20%)。正确行为应是:第 1 单 20%、第 2 单 30%、第 3 单 50%,然后循环。
```1015:1015:pro_v3.5.1/app/services/order/StoreOrderCreateServices.php
$position = max(0, $completedCount - 1) % $cycleCount;
```
**修复方案:**
- 添加数据库级别的锁(如对 spread_uid 维度加 `SELECT ... FOR UPDATE`),序列化周期位次计算
- 或者在用户表中增加显式周期计数器字段(如 `eb_user` 表添加 `brokerage_cycle_position` 列),原子递增,而非依赖 count 查询
- 或者使用 `ORDER BY id ASC` 按订单排名确定位次,而非仅统计总数
---
### B7. e2e 测试验收分销员等级配置(测试问题 #7.
**问题反馈:** "e2e测试验收一下分销员等级中配置的奖励积分直推奖励积分、伞下奖励积分和等级任务是否正确"
**问题分析:** 需要端到端验证 `eb_agent_level` 和 `eb_agent_level_task` 表中的数据配置与 PRD 一致。
**验收清单:**
**1) `eb_agent_level` 表 — 各等级奖励积分配置PRD 3.2**
| 等级 | grade | direct_reward_points直推 | umbrella_reward_points伞下 |
| ---- | ----- | ------------------------ | -------------------------- |
| 创客 | 1 | 500 | 0 |
| 云店 | 2 | 800 | 300 |
| 服务中心 | 3 | 1000 | 200 |
| 合伙人 | 4 | 1300 | 300 |
**2) `eb_agent_level_task` 表 — 各等级升级任务配置PRD 3.2**
| 等级 | 任务类型 type | 任务要求 number | 说明 |
| ---- | --------- | ----------- | ---------- |
| 创客 | 6直推报单单数 | 3 | 直推满 3 单 |
| 云店 | 7伞下报单业绩 | 30 | 伞下满 30 单 |
| 云店 | 8最低直推人数 | 3 | 至少 3 个直推 |
| 服务中心 | 7伞下报单业绩 | 100 | 伞下满 100 单 |
| 服务中心 | 8最低直推人数 | 3 | 至少 3 个直推 |
| 合伙人 | 7伞下报单业绩 | 1000 | 伞下满 1000 单 |
| 合伙人 | 8最低直推人数 | 3 | 至少 3 个直推 |
**3) 功能验证场景(手动或脚本):**
- 创客升级:模拟 3 笔直推报单订单,确认准确在第 3 笔时升级
- 积分发放:确认创客直推积分 500/单、伞下积分 0云店直推 800/单、伞下 300/单
- 佣金轮巡:模拟 3 笔直推报单订单,确认佣金比例依次为 20%→30%→50%
- 批量报单:一个订单含多个报单商品时,积分按商品数量乘算
**修复方案:** 编写 SQL 查询脚本或 PHP 命令行工具验证上述配置,不一致则修正数据。
---
## 文件修改汇总
| 问题编号 | 主要需修改的文件 |
| ---- | ---------------------------------------------------------------------------------------------------------- |
| A1 | `view/uniapp/pages/users/user_member_code/index.vue` |
| A2 | `view/uniapp/pages/users/user_spread_code/index.vue` |
| A3 | `view/uniapp/pages/users/user_bill/index.vue` |
| A4 | `view/uniapp/pages/queue/status.vue` |
| A5 | `view/uniapp/pages/assets/index.vue`、`app/controller/api/v1/hjf/HjfAssets.php` |
| B1 | `app/services/order/StoreOrderCreateServices.php` 或 `app/listener/order/Pay.php` |
| B2 | `app/listener/order/Pay.php`、`app/services/hjf/PointsRewardServices.php` |
| B3 | `app/services/hjf/PointsRewardServices.php`、`app/listener/order/Pay.php`、`app/jobs/hjf/HjfOrderPayJob.php` |
| B4 | `app/services/hjf/PointsRewardServices.php`,核查 `eb_agent_level` 数据 |
| B5 | `app/services/agent/AgentLevelTaskServices.php`,核查 `eb_agent_level_task` 数据 |
| B6 | `app/services/order/StoreOrderCreateServices.php` |
| B7 | 验证脚本 / SQL 查询(验收 `eb_agent_level` + `eb_agent_level_task` 数据配置) |