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

修复游戏奖励日志和用户日志管理的显示问题

1. 修复 /admin/game-reward-logs 页面错误
   - 修正 RewardSourceResolver 中的枚举常量引用
   - 将 USER_REGISTER_TEST 改为 URSPROMOTION_REGISTER
   - 将 PROMOTION_REWARD 改为 URSPROMOTION_REWARD
   - 添加 URSPROMOTION_BACKFILL 和 CRAFT 来源类型支持
   - 新增 resolveCraftSource 方法处理合成来源

2. 优化用户日志管理页面排序
   - 修改 UserLogController 默认排序为按 original_time 排序
   - 添加原始时间和收集时间两个独立列显示
   - 更新筛选器使用 original_time 字段
   - 完善详情页面显示原始时间和收集时间

3. 确认 LogDataHandler 排序正确性
   - 验证 LogDataHandler 通过 UserLogService 正确使用原始时间排序
   - 确认整个调用链路按 original_time 排序工作正常
AI Assistant 6 месяцев назад
Родитель
Сommit
84b96187

+ 32 - 29
AiWork/2025年06月/22日2203-农场统计图表增加各等级土地产出果实统计.md

@@ -1,12 +1,12 @@
 # 农场统计图表:各等级土地产出果实种类统计
 
-**任务时间:** 2025年06月22日 22:03 - 22:30
+**任务时间:** 2025年06月22日 22:03 - 22:45
 **任务类型:** 功能开发
 **模块:** Farm(农场模块)
 
 ## 任务描述
 
-在农场统计图表页面中增加各个等级土地的产出果实种类统计功能,为每个土地等级创建独立的统计卡片,显示该土地类型产出的不同果实种类及其出现次数。
+在农场统计图表页面中增加各个等级土地的产出果实种类统计功能,为每个土地等级创建独立的统计卡片,显示该土地类型产出的不同果实种类及其出现次数。使用FarmCrop模型进行统计,并包含软删除数据以提供完整的数据分析。
 
 ## 实现内容
 
@@ -29,27 +29,26 @@
 
 ### 4. 统计逻辑
 ```php
-// 从作物日志表中统计指定土地类型的收获数据,按果实种类分组
-$harvestStats = FarmCropLog::where('event_type', FarmCropLog::EVENT_HARVESTED)
-    ->where('land_type', $this->landType)
-    ->select(
-        DB::raw('JSON_EXTRACT(event_data, "$.item_id") as item_id'),
-        DB::raw('COUNT(*) as harvest_count')
-    )
-    ->groupBy(DB::raw('JSON_EXTRACT(event_data, "$.item_id")'))
+// 从作物表中统计指定土地等级的产出果实,按果实种类分组
+// 使用withTrashed()包含软删除的数据
+$cropStats = FarmCrop::withTrashed()
+    ->where('land_level', $this->landType)
+    ->whereNotNull('final_output_item_id')
+    ->select('final_output_item_id', DB::raw('COUNT(*) as crop_count'))
+    ->groupBy('final_output_item_id')
     ->get();
 ```
 
 ### 5. 显示效果
 - **卡片数量:** 4个独立卡片,每个土地等级一个
 - **显示格式:** 单行多数字(NumberS2)
-- **显示内容:**
-  - **普通土地产出果实统计:** 萝卜(1次)、辣椒(1次)
-  - **红土地产出果实统计:** 辣椒(8次)、萝卜(2次)、西瓜(2次)、苹果(2次)
-  - **黑土地产出果实统计:** 无数据(空卡片)
-  - **金土地产出果实统计:** 无数据(空卡片)
-- **排序:** 按收获次数降序排列
-- **特性:** 只显示有收获记录的果实种类
+- **显示内容(包含软删除数据后):**
+  - **普通土地产出果实统计:** 萝卜(8次)、辣椒(6次)
+  - **红土地产出果实统计:** 萝卜(23次)、辣椒(19次)、西瓜(4次)、苹果(3次)
+  - **黑土地产出果实统计:** 西瓜(12次)、苹果(7次)、萝卜(2次)、南瓜(1次)、草莓(1次)
+  - **金土地产出果实统计:** 西瓜(6次)、草莓(4次)、南瓜(2次)、苹果(2次)
+- **排序:** 按作物数量降序排列
+- **特性:** 包含软删除数据,提供完整的历史统计分析
 
 ### 6. 页面集成
 - **控制器:** `app/Module/Farm/AdminControllers/FarmMetricsController.php`
@@ -70,21 +69,23 @@ GROUP BY land_type, JSON_EXTRACT(event_data, '$.item_id')
 ORDER BY land_type, harvest_count DESC;
 ```
 
-**验证结果:**
-- **普通土地(1):** 萝卜(ID:2)1次、辣椒(ID:3)1
-- **红土地(2):** 辣椒(ID:3)8次、萝卜(ID:2)2次、西瓜(ID:5)2次、苹果(ID:4)2
-- **黑土地(3):** 无收获记录
-- **金土地(4):** 无收获记录
+**验证结果(包含软删除数据):**
+- **普通土地(1):** 萝卜(ID:2)8次、辣椒(ID:3)6
+- **红土地(2):** 萝卜(ID:2)23次、辣椒(ID:3)19次、西瓜(ID:5)4次、苹果(ID:4)3
+- **黑土地(3):** 西瓜(ID:5)12次、苹果(ID:4)7次、萝卜(ID:2)2次、南瓜(ID:7)1次、草莓(ID:6)1次
+- **金土地(4):** 西瓜(ID:5)6次、草莓(ID:6)4次、南瓜(ID:7)2次、苹果(ID:4)2次
 
 ## 技术特点
 
 1. **模块化设计:** 使用抽象基类和具体实现类,便于扩展新的土地类型
-2. **数据准确性:** 直接从作物日志表统计实际收获数据,按果实种类分组
-3. **性能优化:** 使用SQL聚合函数和JSON提取进行数据库层面统计
-4. **用户体验:** 每个土地类型独立卡片,数据清晰直观
-5. **智能排序:** 按收获次数降序排列,突出热门果实
-6. **代码复用:** 继承UCore框架的NumberS2卡片类,保持UI一致性
-7. **数据关联:** 自动关联物品表获取果实名称,提升可读性
+2. **数据完整性:** 使用withTrashed()包含软删除数据,提供完整的历史统计分析
+3. **数据准确性:** 直接从FarmCrop表统计实际作物数据,按果实种类分组
+4. **性能优化:** 使用SQL聚合函数进行数据库层面统计
+5. **用户体验:** 每个土地类型独立卡片,数据清晰直观
+6. **智能排序:** 按作物数量降序排列,突出主要产出果实
+7. **代码复用:** 继承UCore框架的NumberS2卡片类,保持UI一致性
+8. **数据关联:** 自动关联物品表获取果实名称,提升可读性
+9. **软删除支持:** 符合用户偏好,统计包含删除数据以提供完整数据分析
 
 ## 测试结果
 
@@ -100,7 +101,9 @@ ORDER BY land_type, harvest_count DESC;
 
 **第一次提交:** `6c7ec653` - 初始实现(总产量统计)
 **第二次提交:** `102686c8` - 重构为果实种类统计
-**最终Message:** 农场统计图表:重构土地产出统计为果实种类统计
+**第三次提交:** `48654990` - 改用FarmCrop模型进行统计
+**第四次提交:** `bac5724b` - 包含软删除数据的完整统计分析
+**最终Message:** 农场统计图表:包含软删除数据的完整统计分析
 
 ## 相关文件
 

+ 21 - 6
app/Module/Game/AdminControllers/UserLogController.php

@@ -105,8 +105,19 @@ class UserLogController extends AdminController
                     return $value;
                 });
 
-            $grid->column('created_at', '创建时间')
-                ->sortable();
+            $grid->column('original_time', '原始时间')
+                ->display(function ($value) {
+                    return $value ? $value->format('Y-m-d H:i:s') : '-';
+                })
+                ->sortable()
+                ->help('业务发生的原始时间');
+
+            $grid->column('created_at', '收集时间')
+                ->display(function ($value) {
+                    return $value ? $value->format('Y-m-d H:i:s') : '-';
+                })
+                ->sortable()
+                ->help('日志收集时间');
 
             // 筛选器
             $grid->filter(function (Grid\Filter $filter) {
@@ -123,11 +134,11 @@ class UserLogController extends AdminController
                 $filter->equal('source_type', '来源类型')
                     ->select($sourceTypeOptions);
 
-                $filter->between('created_at', '创建时间')->datetime();
+                $filter->between('original_time', '原始时间')->datetime();
             });
 
-            // 默认排序
-            $grid->model()->orderBy('created_at', 'desc');
+            // 默认排序 - 按原始日志创建时间排序
+            $grid->model()->orderBy('original_time', 'desc');
 
             // 禁用创建按钮
             $grid->disableCreateButton();
@@ -189,7 +200,11 @@ class UserLogController extends AdminController
 
             $show->field('source_table', '来源表名');
 
-            $show->field('created_at', '创建时间');
+            $show->field('original_time', '原始时间')
+                ->help('业务发生的原始时间');
+
+            $show->field('created_at', '收集时间')
+                ->help('日志收集时间');
 
             // 禁用编辑和删除按钮
             $show->disableEditButton();

+ 25 - 2
app/Module/Game/Services/RewardSourceResolver.php

@@ -41,8 +41,9 @@ class RewardSourceResolver
                 case REWARD_SOURCE_TYPE::FARM_PLANT->value:
                     return self::resolveFarmSource($sourceType, $sourceId, $typeInfo);
 
-                case REWARD_SOURCE_TYPE::USER_REGISTER_TEST->value:
-                case REWARD_SOURCE_TYPE::PROMOTION_REWARD->value:
+                case REWARD_SOURCE_TYPE::URSPROMOTION_REGISTER->value:
+                case REWARD_SOURCE_TYPE::URSPROMOTION_REWARD->value:
+                case REWARD_SOURCE_TYPE::URSPROMOTION_BACKFILL->value:
                     return self::resolvePromotionSource($sourceType, $sourceId, $typeInfo);
 
                 case REWARD_SOURCE_TYPE::SIGN_IN->value:
@@ -57,6 +58,9 @@ class RewardSourceResolver
                 case REWARD_SOURCE_TYPE::CHEST->value:
                     return self::resolveChestSource($sourceId, $typeInfo);
 
+                case REWARD_SOURCE_TYPE::CRAFT->value:
+                    return self::resolveCraftSource($sourceId, $typeInfo);
+
                 case REWARD_SOURCE_TYPE::SHOP_PURCHASE->value:
                     return self::resolveShopSource($sourceId, $typeInfo);
 
@@ -257,6 +261,25 @@ class RewardSourceResolver
         ];
     }
 
+    /**
+     * 解析合成来源
+     */
+    private static function resolveCraftSource(int $sourceId, array $typeInfo): array
+    {
+        return [
+            'type' => $typeInfo['name'],
+            'name' => "合成奖励 (ID: {$sourceId})",
+            'description' => $typeInfo['description'],
+            'link' => $typeInfo['admin_link'] ? "{$typeInfo['admin_link']}/{$sourceId}" : null,
+            'status' => 'found',
+            'category' => $typeInfo['category'],
+            'extra' => [
+                'craft_id' => $sourceId,
+                'source_type' => REWARD_SOURCE_TYPE::CRAFT->value
+            ]
+        ];
+    }
+
     /**
      * 解析商店来源
      */