AI Assistant 6 月之前
父节点
当前提交
b5939eb548

+ 176 - 0
AiWork/202507/050009-Promotion-List接口性能优化.md

@@ -0,0 +1,176 @@
+# Promotion-List接口性能优化
+
+## 任务概述
+修复 Promotion-List 接口的严重性能问题,该接口在查询推广团队成员列表时出现超时,执行时间超过2分钟。
+
+## 任务时间
+- **开始时间**:2025年07月05日 00:09:24 CST
+- **完成时间**:2025年07月05日 00:15:00 CST
+- **总耗时**:约6分钟
+
+## 问题分析
+
+### 1. 问题现象
+- 请求:`Promotion-List`,参数:`{"page":{"page":"2","perPage":"10"},"level":"1"}`
+- 执行时间:141168ms(约2.5分钟)
+- 最终超时失败:`cURL error 28: Operation timed out after 30002 milliseconds`
+- 日志显示大量SQL查询:`select urs_user_id from kku_urs_promotion_user_referrals where urs_referrer_id = ? and status = 1`
+
+### 2. 根本原因
+**性能瓶颈在 `getTeamMemberData` 方法**:
+1. 第146行调用 `UrsReferralService::getTeamMembers($ursUserId)` 无参数,默认查询20代
+2. 即使用户只要1代数据(level=1),系统还是查询了20代所有数据
+3. `getTeamMembers()` 方法使用递归查询,对每个用户都调用 `getDirectReferrals()`
+4. 产生数百次数据库查询,造成性能爆炸
+
+### 3. 逻辑问题
+```php
+// 问题代码
+$teamMembers = UrsReferralService::getTeamMembers($ursUserId); // 查询20代
+if ($level == 1) {
+    // 只使用1代数据,其他19代查询都是浪费
+    $filteredMembers = $teamMembers[1];
+}
+```
+
+## 解决方案
+
+### 1. 使用缓存表优化查询
+将递归查询改为使用 `UrsUserRelationCache` 缓存表:
+
+**优化前**:
+```php
+// 递归查询20代,产生数百次SQL查询
+$teamMembers = UrsReferralService::getTeamMembers($ursUserId);
+```
+
+**优化后**:
+```php
+// 直接查询缓存表,只查询指定层级
+$query = UrsUserRelationCache::where('related_user_id', $farmUserId);
+if ($level == 1) {
+    $query->where('depth', 1); // 只查询1代
+}
+```
+
+### 2. 数据库层面分页
+**优化前**:查询所有数据后在内存中分页
+**优化后**:在数据库层面进行分页查询
+
+### 3. 精确查询层级
+根据 `level` 参数精确查询指定层级,避免不必要的数据查询。
+
+## 修改内容
+
+### 1. 核心方法重构
+文件:`app/Module/AppGame/Handler/Promotion/ListHandler.php`
+
+**主要修改**:
+- 重构 `getTeamMemberData()` 方法
+- 使用 `UrsUserRelationCache` 缓存表替代递归查询
+- 根据 `level` 参数精确查询指定层级
+- 在数据库层面进行分页
+- 移除不再使用的导入
+
+**关键查询优化**:
+```sql
+-- 统计总数
+SELECT COUNT(*) FROM kku_urs_promotion_user_relation_cache 
+WHERE related_user_id = ? AND depth = ?
+
+-- 分页查询
+SELECT * FROM kku_urs_promotion_user_relation_cache 
+WHERE related_user_id = ? AND depth = ? 
+ORDER BY depth ASC, user_id ASC 
+LIMIT ? OFFSET ?
+```
+
+## 性能提升
+
+### 1. 执行时间对比
+- **优化前**:141168ms(约2.5分钟)
+- **优化后**:3717ms(约3.7秒)
+- **性能提升**:约 **38倍**
+
+### 2. SQL查询对比
+- **优化前**:数百次递归查询 `urs_promotion_user_referrals` 表
+- **优化后**:2次查询 `urs_promotion_user_relation_cache` 表(count + select)
+
+### 3. 资源消耗
+- **优化前**:大量数据库连接和查询,内存占用高
+- **优化后**:最小化数据库查询,内存占用低
+
+## 测试验证
+
+### 1. 功能测试
+使用 `php artisan debug:reproduce-error 69223552` 重放原始请求:
+- ✅ 请求成功返回
+- ✅ 数据正确:total=54,返回第2页10条数据
+- ✅ 分页信息正确:currentPage=2, perPage=10, hasMore=true, lastPage=6
+
+### 2. 性能测试
+- ✅ 执行时间从141秒降低到3.7秒
+- ✅ SQL查询从数百次降低到2次
+- ✅ 无超时错误
+
+## 技术要点
+
+### 1. 缓存表设计
+`UrsUserRelationCache` 表的设计很好地支持了这种查询优化:
+- `related_user_id`:上级用户ID,用于查询某用户的所有下级
+- `depth`:层级深度,支持精确查询指定层级
+- 索引优化:支持高效的分页查询
+
+### 2. 查询策略
+- 避免N+1查询问题
+- 使用数据库层面的分页和排序
+- 根据业务需求精确查询,避免过度查询
+
+### 3. 向后兼容
+- 保持API接口不变
+- 返回数据格式完全一致
+- 不影响其他功能模块
+
+## 业务影响
+
+### 1. 用户体验提升
+- 推广列表加载速度大幅提升
+- 避免接口超时导致的用户体验问题
+- 提高系统响应性能
+
+### 2. 系统稳定性
+- 减少数据库压力
+- 降低系统资源消耗
+- 提高并发处理能力
+
+### 3. 可扩展性
+- 为后续功能扩展奠定基础
+- 缓存表设计支持更复杂的查询需求
+
+## 提交记录
+
+**Commit**: eb917d51
+```
+优化Promotion-List接口性能:使用缓存表替代递归查询
+
+- 修改getTeamMemberData方法,使用UrsUserRelationCache缓存表查询
+- 避免递归调用getTeamMembers方法查询20代数据的性能问题
+- 根据level参数精确查询指定层级,避免不必要的数据查询
+- 在数据库层面进行分页,提升查询效率
+- 性能提升约38倍:从141秒优化到3.7秒
+- 移除不再使用的UrsReferralService和AccountService导入
+```
+
+## 后续建议
+
+### 1. 监控优化效果
+- 持续监控 Promotion-List 接口的性能表现
+- 关注缓存表的数据一致性
+
+### 2. 类似问题排查
+- 检查其他使用 `UrsReferralService::getTeamMembers()` 的地方
+- 评估是否需要类似的优化
+
+### 3. 缓存表维护
+- 确保 `UrsUserRelationCache` 表的数据及时更新
+- 监控缓存表的数据完整性

+ 26 - 0
AiWork/now.md

@@ -100,6 +100,32 @@
 - ✅ 筛选功能正常工作,可以筛选铲除事件
 - ✅ 事件数据完整,包含工具ID、时间、状态变化等信息
 
+### 6. Promotion-List接口性能优化 (2025-07-05 00:15)
+- 修复Promotion-List接口执行时间超过2分钟的严重性能问题
+- 使用UrsUserRelationCache缓存表替代递归查询20代团队数据
+- 根据level参数精确查询指定层级,避免不必要的数据查询
+- 在数据库层面进行分页,提升查询效率
+- 性能提升约38倍:从141秒优化到3.7秒
+- Commit: eb917d51
+
+#### 问题分析
+**问题**: 请求Promotion-List时执行时间141秒,最终超时失败
+**原因**:
+1. getTeamMemberData方法调用getTeamMembers()无参数,默认查询20代
+2. 即使用户只要1代数据,系统还是递归查询了20代所有数据
+3. 产生数百次SQL查询,造成性能爆炸
+
+#### 解决方案
+- 使用UrsUserRelationCache缓存表直接查询指定层级
+- 避免递归调用和N+1查询问题
+- 数据库层面分页,减少内存占用
+- SQL查询从数百次优化到2次(count + select)
+
+#### 性能对比
+- **执行时间**: 141168ms → 3717ms (提升38倍)
+- **SQL查询**: 数百次递归查询 → 2次缓存表查询
+- **功能验证**: ✅ 数据正确,分页正常,接口稳定
+
 ## 当前状态
 任务已完成,等待用户验收和新任务。
 

+ 23 - 6
app/Module/Farm/Commands/FixLandStatusCommand.php

@@ -180,17 +180,24 @@ class FixLandStatusCommand extends Command
     /**
      * 统计活跃灾害数量
      *
-     * @param string|null $disasters
+     * @param string|array|null $disasters
      * @return int
      */
-    private function countActiveDisasters(?string $disasters): int
+    private function countActiveDisasters($disasters): int
     {
         if (empty($disasters)) {
             return 0;
         }
 
-        $disasterArray = json_decode($disasters, true);
-        if (!is_array($disasterArray)) {
+        // 如果是字符串,解码为数组
+        if (is_string($disasters)) {
+            $disasterArray = json_decode($disasters, true);
+            if (!is_array($disasterArray)) {
+                return 0;
+            }
+        } elseif (is_array($disasters)) {
+            $disasterArray = $disasters;
+        } else {
             return 0;
         }
 
@@ -295,6 +302,16 @@ class FixLandStatusCommand extends Command
                 'problem_type' => $problemType,
                 'active_disasters' => $this->countActiveDisasters($crop->disasters)
             ]);
+        }else{
+            Log::info('土地状态无需修复', [
+                'land_id' => $land->id,
+                'user_id' => $land->user_id,
+                'crop_id' => $crop->id,
+                'status' => $land->status,
+                'growth_stage' => $crop->growth_stage,
+                'problem_type' => $problemType,
+                'active_disasters' => $this->countActiveDisasters($crop->disasters)
+            ]);
         }
     }
 
@@ -308,11 +325,11 @@ class FixLandStatusCommand extends Command
     {
         // 根据作物生长阶段确定土地状态
         switch ($crop->growth_stage) {
-            case GROWTH_STAGE::MATURE->value:
+            case GROWTH_STAGE::MATURE:
                 // 作物成熟,土地状态为可收获
                 return LAND_STATUS::HARVESTABLE->value;
                 
-            case GROWTH_STAGE::WITHERED->value:
+            case GROWTH_STAGE::WITHERED:
                 // 作物枯萎,土地状态为枯萎
                 return LAND_STATUS::WITHERED->value;