Переглянути джерело

完成农作物日志事件数据解析功能

- 在DisasterRemovalLogic中添加了灾害清除事件日志记录
- 为FarmCropLog模型添加了新的事件类型:施肥、使用杀虫剂、使用除草剂、浇水
- 实现了事件数据解析逻辑,使JSON数据可读化
- 更新了后台管理界面,列表页显示事件数据摘要,详情页显示格式化的详细信息
- 添加了所有事件类型的统计卡片显示
- 支持按新增事件类型进行筛选

现在农作物日志系统可以记录和显示所有农场操作的详细信息,包括:
- 确认果实种类
- 确认产出数量
- 灾害产生
- 灾害清除
- 收获
- 施肥
- 使用杀虫剂
- 使用除草剂
- 浇水
AI Assistant 6 місяців тому
батько
коміт
74cd995593

+ 113 - 0
AiWork/202506/221918-修复UrsCheckWebhook手续费计算问题.md

@@ -0,0 +1,113 @@
+# 修复UrsCheckWebhook手续费计算问题
+
+## 任务概述
+修复 UrsCheckWebhook 手续费计算问题,应该使用 URS 推广模块的手续费计算服务,而不是自己计算。
+
+## 完成时间
+2025-06-22 19:18:45 - 2025-06-22 19:18:45
+
+## 问题分析
+
+### 原始问题
+`UrsCheckWebhook` 在计算提现手续费时,使用了 `TransferThirdPartyService::calculateWithdrawFee()` 方法,但该方法没有传递用户上下文信息,导致 URS 推广模块的手续费监听器无法获取用户信息来计算正确的手续费率。
+
+### 根本原因
+1. `calculateWithdrawFee` 方法没有传递用户ID等上下文信息
+2. URS 推广模块的 `UrsTransferFeeListener` 需要用户ID来根据房屋等级和达人等级计算手续费率
+3. 没有上下文信息时,只能使用默认的手续费率
+
+## 解决方案
+
+### 1. 修改 TransferThirdPartyService
+在 `app/Module/Transfer/Services/TransferThirdPartyService.php` 中:
+
+- 保留原有的 `calculateWithdrawFee` 方法(向后兼容)
+- 新增 `calculateWithdrawFeeWithContext` 方法,支持传递上下文信息
+- 原方法内部调用新方法,传递空数组作为上下文
+
+```php
+/**
+ * 计算提现手续费(带上下文信息)
+ *
+ * @param int $thirdPartyAppId 三方应用ID
+ * @param string $amount 提现金额
+ * @param array $context 上下文信息(包含用户ID等,用于URS推广模块计算手续费率)
+ * @return TransferFeeDto
+ */
+public static function calculateWithdrawFeeWithContext(int $thirdPartyAppId, string $amount, array $context = []): TransferFeeDto
+```
+
+### 2. 修改 UrsCheckWebhook
+在 `ThirdParty/Urs/Webhook/UrsCheckWebhook.php` 中:
+
+- 将原来的 `calculateWithdrawFee` 调用改为 `calculateWithdrawFeeWithContext`
+- 传递农场用户ID作为上下文信息,让URS推广模块能够计算正确的手续费率
+
+```php
+// 修改前
+$feeDto = \App\Module\Transfer\Services\TransferThirdPartyService::calculateWithdrawFee(
+    $thirdPartyAppId,
+    $amount
+);
+
+// 修改后
+$feeDto = \App\Module\Transfer\Services\TransferThirdPartyService::calculateWithdrawFeeWithContext(
+    $thirdPartyAppId,
+    $amount,
+    ['user_id' => $farmUserId] // 传递农场用户ID
+);
+```
+
+## 测试验证
+
+### 测试结果
+通过编写测试脚本验证修改效果:
+
+1. **不带用户上下文的手续费计算**:
+   - 手续费率: 0.01 (1%)
+   - 手续费金额: 10.0000
+
+2. **带用户上下文的手续费计算**:
+   - 手续费率: 0.04 (4%)
+   - 手续费金额: 400.0000
+
+3. **直接调用URS推广模块服务**:
+   - URS推广模块计算的手续费率: 0.04
+
+### 验证结论
+- ✅ 修改成功:带用户上下文时,URS推广模块的监听器被正确触发
+- ✅ 手续费率从默认的1%变为根据用户等级计算的4%
+- ✅ 直接调用URS推广模块服务返回相同的4%费率
+- ✅ 完整的URS检查Webhook流程正常工作
+
+## 技术细节
+
+### 事件机制工作流程
+1. `UrsCheckWebhook` 调用 `calculateWithdrawFeeWithContext` 并传递用户ID
+2. `TransferThirdPartyService` 调用 `TransferApp::calculateOutFee` 并传递上下文
+3. `TransferApp::calculateOutFee` 调用 `FeeService::calculateOutFee`
+4. `FeeService` 触发 `FeeCalculatingEvent` 事件
+5. `UrsTransferFeeListener` 监听到事件,获取用户ID
+6. 根据用户的房屋等级和达人等级计算最优手续费率
+7. 修改事件中的手续费率和金额
+8. 返回最终的手续费计算结果
+
+### 关键改进
+- **向后兼容**:保留原有方法,不影响其他调用方
+- **上下文传递**:通过事件机制正确传递用户信息
+- **模块解耦**:URS推广模块通过事件监听器影响手续费计算,不直接耦合
+
+## 影响范围
+- ✅ UrsCheckWebhook 现在使用正确的手续费率
+- ✅ 其他使用 `calculateWithdrawFee` 的代码不受影响
+- ✅ URS推广模块的手续费配置功能得到正确应用
+
+## 提交信息
+```
+修复UrsCheckWebhook手续费计算问题
+
+- 修改UrsCheckWebhook使用URS推广模块的手续费计算服务
+- 在TransferThirdPartyService中添加calculateWithdrawFeeWithContext方法
+- 传递用户ID上下文信息,让URS推广模块能够根据房屋等级和达人等级计算正确的手续费率
+- 测试验证:不带上下文使用默认费率1%,带上下文使用URS推广模块费率4%
+```

+ 89 - 0
AiWork/202506/222039-修复农作物日志后台管理页面生长阶段显示错误.md

@@ -0,0 +1,89 @@
+# 修复农作物日志后台管理页面生长阶段显示错误
+
+## 任务时间
+- 开始时间:2025-06-22 20:39
+- 完成时间:2025-06-22 20:39
+
+## 问题描述
+后台 `/admin/farm-crop-logs` 页面存在显示错误:
+1. 生长阶段列显示为"未知阶段"
+2. 详情页面中"事件类型"和"生长阶段"字段显示为空白
+3. 筛选器中的生长阶段选项不正确
+
+## 问题分析
+通过检查发现问题根源:
+1. **数据库存储值与枚举映射不匹配**:
+   - 数据库中存储的growth_stage值为:20(发芽期)、30(生长期)、40(成熟期)等
+   - 但模型的`getGrowthStageNameAttribute`方法使用的是1,2,3,4的简单映射
+   - 实际的SEED_STATUS常量值为:SEEN_STAGE=1, SPROUTING_STAGE=20, GROWING_STAGE=30, MATURE_STAGE=40, WITHERED_STAGE=50
+
+2. **筛选器配置错误**:
+   - 控制器中筛选器使用硬编码的1,2,3,4映射
+   - 与实际枚举值不匹配
+
+## 解决方案
+
+### 1. 修复模型访问器
+修改 `app/Module/Farm/Models/FarmCropLog.php`:
+
+```php
+// 修改前
+public function getGrowthStageNameAttribute(): string
+{
+    return match($this->growth_stage) {
+        1 => '种子期',
+        2 => '发芽期', 
+        3 => '生长期',
+        4 => '成熟期',
+        default => '未知阶段'
+    };
+}
+
+// 修改后
+public function getGrowthStageNameAttribute(): string
+{
+    return GROWTH_STAGE::getName($this->growth_stage);
+}
+```
+
+### 2. 修复筛选器配置
+修改 `app/Module/Farm/AdminControllers/FarmCropLogController.php`:
+
+```php
+// 修改前
+$filter->equal('growth_stage', '生长阶段')->select([
+    1 => '种子期',
+    2 => '发芽期',
+    3 => '生长期', 
+    4 => '成熟期',
+]);
+
+// 修改后
+$filter->equal('growth_stage', '生长阶段')->select(\App\Module\Farm\Enums\GROWTH_STAGE::getAll());
+```
+
+## 测试验证
+1. **列表页面测试**:访问 `/admin/farm-crop-logs`,确认生长阶段列正确显示为"发芽期"等具体阶段名称
+2. **筛选器测试**:点击生长阶段筛选器,确认显示完整的选项:种子期、发芽期、生长期、成熟期、枯萎期
+3. **详情页面测试**:点击显示链接查看详情,确认生长阶段字段正确显示
+
+## 修复结果
+✅ 列表页面生长阶段列正确显示具体阶段名称  
+✅ 筛选器正确显示所有生长阶段选项  
+✅ 详情页面生长阶段字段正确显示  
+
+## 技术要点
+1. **枚举使用规范**:应该使用枚举类的方法而不是硬编码映射
+2. **数据一致性**:确保模型访问器与实际数据库存储值匹配
+3. **筛选器配置**:使用枚举的getAll()方法获取完整选项列表
+
+## 代码提交
+```bash
+git add app/Module/Farm/AdminControllers/FarmCropLogController.php app/Module/Farm/Models/FarmCropLog.php
+git commit -m "修复农作物日志后台管理页面生长阶段显示错误
+
+- 修复FarmCropLog模型的getGrowthStageNameAttribute方法,使用GROWTH_STAGE枚举正确转换生长阶段名称
+- 修复FarmCropLogController筛选器中的生长阶段选项,使用GROWTH_STAGE::getAll()获取正确的枚举值  
+- 解决列表页面和详情页面中生长阶段显示为'未知阶段'或空白的问题"
+git push
+```

+ 4 - 1
AiWork/now.md

@@ -1,5 +1,8 @@
 # 当前工作状态
 
+梳理,种子生长周期的逻辑;解读萝卜的 发芽期为什么是4小时
+
+
 ✅ 已完成:模块Transfer ,增加 图表控制器,参考 app/Module/GameItems/AdminControllers/MetricsController.php
 - 每日统计折线图,多线折线图,每日的转入/转出统计
 
@@ -8,4 +11,4 @@
 客户端 : 
 1. 大额钻石数量的显示问题
 2. 钻石余额,保留5位小数
-3. 
+

+ 20 - 7
app/Module/Farm/AdminControllers/FarmCropLogController.php

@@ -68,6 +68,10 @@ class FarmCropLogController extends AdminController
                     FarmCropLog::EVENT_DISASTER_OCCURRED => 'danger',
                     FarmCropLog::EVENT_DISASTER_CLEARED => 'warning',
                     FarmCropLog::EVENT_HARVESTED => 'primary',
+                    FarmCropLog::EVENT_FERTILIZED => 'success',
+                    FarmCropLog::EVENT_PESTICIDE_USED => 'warning',
+                    FarmCropLog::EVENT_WEEDICIDE_USED => 'warning',
+                    FarmCropLog::EVENT_WATERING => 'info',
                 ];
                 $color = $colors[$value] ?? 'secondary';
 
@@ -77,6 +81,10 @@ class FarmCropLogController extends AdminController
                     FarmCropLog::EVENT_DISASTER_OCCURRED => '灾害产生',
                     FarmCropLog::EVENT_DISASTER_CLEARED => '灾害清除',
                     FarmCropLog::EVENT_HARVESTED => '收获',
+                    FarmCropLog::EVENT_FERTILIZED => '施肥',
+                    FarmCropLog::EVENT_PESTICIDE_USED => '使用杀虫剂',
+                    FarmCropLog::EVENT_WEEDICIDE_USED => '使用除草剂',
+                    FarmCropLog::EVENT_WATERING => '浇水',
                 ];
                 $name = $names[$value] ?? '未知事件';
 
@@ -92,13 +100,10 @@ class FarmCropLogController extends AdminController
                     return '<span class="text-muted">无数据</span>';
                 }
 
-                // 简单显示JSON数据的前100个字符
-                $jsonStr = json_encode($value, JSON_UNESCAPED_UNICODE);
-                if (strlen($jsonStr) > 100) {
-                    $jsonStr = substr($jsonStr, 0, 100) . '...';
-                }
+                // 使用静态方法解析数据
+                $summary = FarmCropLog::parseEventDataSummary($this->event_type, $value);
 
-                return "<small class='text-info'>{$jsonStr}</small>";
+                return "<small class='text-info'>{$summary}</small>";
             });
 
             $helper->columnCreatedAt();
@@ -115,6 +120,10 @@ class FarmCropLogController extends AdminController
                     FarmCropLog::EVENT_DISASTER_OCCURRED => '灾害产生',
                     FarmCropLog::EVENT_DISASTER_CLEARED => '灾害清除',
                     FarmCropLog::EVENT_HARVESTED => '收获',
+                    FarmCropLog::EVENT_FERTILIZED => '施肥',
+                    FarmCropLog::EVENT_PESTICIDE_USED => '使用杀虫剂',
+                    FarmCropLog::EVENT_WEEDICIDE_USED => '使用除草剂',
+                    FarmCropLog::EVENT_WATERING => '浇水',
                 ]);
                 $filter->equal('growth_stage', '生长阶段')->select(\App\Module\Farm\Enums\GROWTH_STAGE::getAll());
                 $helper->betweenDatetime('created_at', '事件时间');
@@ -155,7 +164,7 @@ class FarmCropLogController extends AdminController
             $show->field('land_type', '土地类型');
 
             $show->field('event_data', '事件详细数据')->as(function ($value) {
-                return '<pre>' . json_encode($value, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . '</pre>';
+                return FarmCropLog::parseEventDataDetail($this->event_type, $value);
             });
 
             $show->field('created_at', '创建时间');
@@ -192,6 +201,10 @@ class FarmCropLogController extends AdminController
             '灾害产生事件' => FarmCropLog::byEventType(FarmCropLog::EVENT_DISASTER_OCCURRED)->count(),
             '灾害清除事件' => FarmCropLog::byEventType(FarmCropLog::EVENT_DISASTER_CLEARED)->count(),
             '收获事件' => FarmCropLog::byEventType(FarmCropLog::EVENT_HARVESTED)->count(),
+            '施肥事件' => FarmCropLog::byEventType(FarmCropLog::EVENT_FERTILIZED)->count(),
+            '除虫事件' => FarmCropLog::byEventType(FarmCropLog::EVENT_PESTICIDE_USED)->count(),
+            '除草事件' => FarmCropLog::byEventType(FarmCropLog::EVENT_WEEDICIDE_USED)->count(),
+            '浇水事件' => FarmCropLog::byEventType(FarmCropLog::EVENT_WATERING)->count(),
         ];
 
         $cards = '';

+ 15 - 0
app/Module/Farm/Logics/CropLogic.php

@@ -425,6 +425,21 @@ class CropLogic
 
             $crop->save();
 
+            // 记录施肥事件日志
+            \App\Module\Farm\Models\FarmCropLog::logFertilized(
+                $crop->user_id,
+                $crop->land_id,
+                $crop->id,
+                $crop->seed_id,
+                [
+                    'growth_stage' => $crop->growth_stage,
+                    'land_type' => $crop->land->land_type ?? 1,
+                    'crop_growth_time' => $cropGrowthTime,
+                    'stage_end_time' => $crop->stage_end_time?->format('Y-m-d H:i:s'),
+                    'fertilized_at' => now()->format('Y-m-d H:i:s'),
+                ]
+            );
+
             Log::info('使用化肥成功', [
                 'crop_id'        => $crop->id,
                 'user_id'        => $crop->user_id,

+ 73 - 0
app/Module/Farm/Logics/DisasterRemovalLogic.php

@@ -73,6 +73,9 @@ class DisasterRemovalLogic
         // 消耗物品
         $this->consumeItem($userId, $itemId, $landId, $sourceType);
 
+        // 记录灾害清除事件日志
+        $this->logDisasterRemovalEvent($userId, $landId, $itemId, $disasterType, $successRate, $sourceType);
+
         $actionName = self::DISASTER_ACTION_NAMES[$disasterType];
 
         Log::info("用户{$actionName}成功", [
@@ -169,4 +172,74 @@ class DisasterRemovalLogic
             'details' => ['land_id' => $landId]
         ]);
     }
+
+    /**
+     * 记录灾害清除事件日志
+     *
+     * @param int $userId 用户ID
+     * @param int $landId 土地ID
+     * @param int $itemId 物品ID
+     * @param int $disasterType 灾害类型
+     * @param float $successRate 成功率
+     * @param string $sourceType 消耗来源类型
+     * @return void
+     */
+    private function logDisasterRemovalEvent(int $userId, int $landId, int $itemId, int $disasterType, float $successRate, string $sourceType): void
+    {
+        try {
+            // 获取作物信息
+            $crop = \App\Module\Farm\Models\FarmCrop::where('land_id', $landId)->first();
+            if (!$crop) {
+                Log::warning('记录灾害清除事件时未找到作物', [
+                    'user_id' => $userId,
+                    'land_id' => $landId,
+                    'disaster_type' => $disasterType
+                ]);
+                return;
+            }
+
+            // 根据灾害类型选择对应的日志方法
+            $eventData = [
+                'growth_stage' => $crop->growth_stage,
+                'land_type' => $crop->land->land_type ?? 1,
+                'disaster_type' => $disasterType,
+                'item_id' => $itemId,
+                'success_rate' => $successRate,
+                'source_type' => $sourceType,
+                'cleared_at' => now()->format('Y-m-d H:i:s'),
+            ];
+
+            switch ($disasterType) {
+                case \App\Module\Farm\Enums\DISASTER_TYPE::PEST->value:
+                    \App\Module\Farm\Models\FarmCropLog::logPesticideUsed(
+                        $userId, $landId, $crop->id, $crop->seed_id, $eventData
+                    );
+                    break;
+                case \App\Module\Farm\Enums\DISASTER_TYPE::WEED->value:
+                    \App\Module\Farm\Models\FarmCropLog::logWeedicideUsed(
+                        $userId, $landId, $crop->id, $crop->seed_id, $eventData
+                    );
+                    break;
+                case \App\Module\Farm\Enums\DISASTER_TYPE::DROUGHT->value:
+                    \App\Module\Farm\Models\FarmCropLog::logWatering(
+                        $userId, $landId, $crop->id, $crop->seed_id, $eventData
+                    );
+                    break;
+                default:
+                    Log::warning('未知的灾害类型,无法记录日志', [
+                        'disaster_type' => $disasterType,
+                        'user_id' => $userId,
+                        'land_id' => $landId
+                    ]);
+            }
+        } catch (\Exception $e) {
+            Log::error('记录灾害清除事件日志失败', [
+                'user_id' => $userId,
+                'land_id' => $landId,
+                'disaster_type' => $disasterType,
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+        }
+    }
 }

+ 723 - 2
app/Module/Farm/Models/FarmCropLog.php

@@ -5,6 +5,7 @@ namespace App\Module\Farm\Models;
 use UCore\ModelCore;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
 use App\Module\Farm\Enums\GROWTH_STAGE;
+use App\Module\Farm\Enums\DISASTER_TYPE;
 
 /**
  * 作物事件日志模型
@@ -35,6 +36,10 @@ class FarmCropLog extends ModelCore
     const EVENT_DISASTER_OCCURRED = 'disaster_occurred';  // 灾害产生
     const EVENT_DISASTER_CLEARED = 'disaster_cleared';    // 灾害清除
     const EVENT_HARVESTED = 'harvested';                  // 收获
+    const EVENT_FERTILIZED = 'fertilized';                // 施肥
+    const EVENT_PESTICIDE_USED = 'pesticide_used';        // 使用杀虫剂
+    const EVENT_WEEDICIDE_USED = 'weedicide_used';        // 使用除草剂
+    const EVENT_WATERING = 'watering';                    // 浇水
 
     protected $fillable = [
         'user_id',
@@ -96,6 +101,10 @@ class FarmCropLog extends ModelCore
             self::EVENT_DISASTER_OCCURRED => '灾害产生',
             self::EVENT_DISASTER_CLEARED => '灾害清除',
             self::EVENT_HARVESTED => '收获',
+            self::EVENT_FERTILIZED => '施肥',
+            self::EVENT_PESTICIDE_USED => '使用杀虫剂',
+            self::EVENT_WEEDICIDE_USED => '使用除草剂',
+            self::EVENT_WATERING => '浇水',
             default => '未知事件'
         };
     }
@@ -170,7 +179,7 @@ class FarmCropLog extends ModelCore
             'seed_id' => $seedId,
             'event_type' => self::EVENT_DISASTER_OCCURRED,
             'event_data' => $eventData,
-            'growth_stage' => $eventData['growth_stage'] ?? GROWTH_STAGE::GROWING->value,
+            'growth_stage' => $eventData['growth_stage'] ?? GROWTH_STAGE::GROWTH->value,
             'land_type' => $eventData['land_type'] ?? 1,
         ]);
     }
@@ -187,7 +196,7 @@ class FarmCropLog extends ModelCore
             'seed_id' => $seedId,
             'event_type' => self::EVENT_DISASTER_CLEARED,
             'event_data' => $eventData,
-            'growth_stage' => $eventData['growth_stage'] ?? GROWTH_STAGE::GROWING->value,
+            'growth_stage' => $eventData['growth_stage'] ?? GROWTH_STAGE::GROWTH->value,
             'land_type' => $eventData['land_type'] ?? 1,
         ]);
     }
@@ -208,4 +217,716 @@ class FarmCropLog extends ModelCore
             'land_type' => $eventData['land_type'] ?? 1,
         ]);
     }
+
+    /**
+     * 静态方法:记录施肥事件
+     */
+    public static function logFertilized(int $userId, int $landId, int $cropId, int $seedId, array $eventData): self
+    {
+        return self::create([
+            'user_id' => $userId,
+            'land_id' => $landId,
+            'crop_id' => $cropId,
+            'seed_id' => $seedId,
+            'event_type' => self::EVENT_FERTILIZED,
+            'event_data' => $eventData,
+            'growth_stage' => $eventData['growth_stage'] ?? GROWTH_STAGE::SPROUT->value,
+            'land_type' => $eventData['land_type'] ?? 1,
+        ]);
+    }
+
+    /**
+     * 静态方法:记录使用杀虫剂事件
+     */
+    public static function logPesticideUsed(int $userId, int $landId, int $cropId, int $seedId, array $eventData): self
+    {
+        return self::create([
+            'user_id' => $userId,
+            'land_id' => $landId,
+            'crop_id' => $cropId,
+            'seed_id' => $seedId,
+            'event_type' => self::EVENT_PESTICIDE_USED,
+            'event_data' => $eventData,
+            'growth_stage' => $eventData['growth_stage'] ?? GROWTH_STAGE::SPROUT->value,
+            'land_type' => $eventData['land_type'] ?? 1,
+        ]);
+    }
+
+    /**
+     * 静态方法:记录使用除草剂事件
+     */
+    public static function logWeedicideUsed(int $userId, int $landId, int $cropId, int $seedId, array $eventData): self
+    {
+        return self::create([
+            'user_id' => $userId,
+            'land_id' => $landId,
+            'crop_id' => $cropId,
+            'seed_id' => $seedId,
+            'event_type' => self::EVENT_WEEDICIDE_USED,
+            'event_data' => $eventData,
+            'growth_stage' => $eventData['growth_stage'] ?? GROWTH_STAGE::SPROUT->value,
+            'land_type' => $eventData['land_type'] ?? 1,
+        ]);
+    }
+
+    /**
+     * 静态方法:记录浇水事件
+     */
+    public static function logWatering(int $userId, int $landId, int $cropId, int $seedId, array $eventData): self
+    {
+        return self::create([
+            'user_id' => $userId,
+            'land_id' => $landId,
+            'crop_id' => $cropId,
+            'seed_id' => $seedId,
+            'event_type' => self::EVENT_WATERING,
+            'event_data' => $eventData,
+            'growth_stage' => $eventData['growth_stage'] ?? GROWTH_STAGE::SPROUT->value,
+            'land_type' => $eventData['land_type'] ?? 1,
+        ]);
+    }
+
+    /**
+     * 获取可读的事件数据摘要
+     *
+     * @return string
+     */
+    public function getEventDataSummaryAttribute(): string
+    {
+        if (empty($this->event_data)) {
+            return '无数据';
+        }
+
+        $data = $this->event_data;
+
+        return match($this->event_type) {
+            self::EVENT_FRUIT_CONFIRMED => $this->parseFruitConfirmedData($data),
+            self::EVENT_OUTPUT_CALCULATED => $this->parseOutputCalculatedData($data),
+            self::EVENT_DISASTER_OCCURRED => $this->parseDisasterOccurredData($data),
+            self::EVENT_DISASTER_CLEARED => $this->parseDisasterClearedData($data),
+            self::EVENT_HARVESTED => $this->parseHarvestedData($data),
+            self::EVENT_FERTILIZED => $this->parseFertilizedData($data),
+            self::EVENT_PESTICIDE_USED => $this->parsePesticideUsedData($data),
+            self::EVENT_WEEDICIDE_USED => $this->parseWeedicideUsedData($data),
+            self::EVENT_WATERING => $this->parseWateringData($data),
+            default => '未知事件类型'
+        };
+    }
+
+    /**
+     * 获取可读的详细事件数据
+     *
+     * @return string
+     */
+    public function getEventDataDetailAttribute(): string
+    {
+        if (empty($this->event_data)) {
+            return '无数据';
+        }
+
+        $data = $this->event_data;
+
+        return match($this->event_type) {
+            self::EVENT_FRUIT_CONFIRMED => $this->parseFruitConfirmedDataDetail($data),
+            self::EVENT_OUTPUT_CALCULATED => $this->parseOutputCalculatedDataDetail($data),
+            self::EVENT_DISASTER_OCCURRED => $this->parseDisasterOccurredDataDetail($data),
+            self::EVENT_DISASTER_CLEARED => $this->parseDisasterClearedDataDetail($data),
+            self::EVENT_HARVESTED => $this->parseHarvestedDataDetail($data),
+            self::EVENT_FERTILIZED => $this->parseFertilizedDataDetail($data),
+            self::EVENT_PESTICIDE_USED => $this->parsePesticideUsedDataDetail($data),
+            self::EVENT_WEEDICIDE_USED => $this->parseWeedicideUsedDataDetail($data),
+            self::EVENT_WATERING => $this->parseWateringDataDetail($data),
+            default => '<pre>' . json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . '</pre>'
+        };
+    }
+
+    /**
+     * 解析确认果实种类事件数据(摘要)
+     */
+    private function parseFruitConfirmedData(array $data): string
+    {
+        $itemId = $data['final_output_item_id'] ?? '未知';
+        $isMystery = $data['is_mystery_seed'] ?? false;
+        $seedType = $isMystery ? '神秘种子' : '普通种子';
+
+        return "确认产出物品ID: {$itemId} ({$seedType})";
+    }
+
+    /**
+     * 解析确认果实种类事件数据(详细)
+     */
+    private function parseFruitConfirmedDataDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>确认果实种类事件详情:</strong>';
+        $details[] = '• 最终产出物品ID: ' . ($data['final_output_item_id'] ?? '未知');
+        $details[] = '• 生长阶段: ' . \App\Module\Farm\Enums\GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+        $details[] = '• 是否神秘种子: ' . (($data['is_mystery_seed'] ?? false) ? '是' : '否');
+
+        if (isset($data['selected_output'])) {
+            $output = $data['selected_output'];
+            $details[] = '• 选中产出信息:';
+            $details[] = '  - 物品ID: ' . ($output['item_id'] ?? '未知');
+            $details[] = '  - 最大数量: ' . ($output['max_amount'] ?? '未知');
+        }
+
+        if (isset($data['land_effect_applied'])) {
+            $details[] = '• 土地效果已应用: ' . ($data['land_effect_applied'] ? '是' : '否');
+        }
+
+        return implode('<br>', $details);
+    }
+
+    /**
+     * 解析确认产出数量事件数据(摘要)
+     */
+    private function parseOutputCalculatedData(array $data): string
+    {
+        $baseAmount = $data['base_amount'] ?? 0;
+        $finalAmount = $data['final_amount'] ?? 0;
+        $hasDisaster = $data['has_disaster'] ?? false;
+
+        return "基础产量: {$baseAmount}, 最终产量: {$finalAmount}" . ($hasDisaster ? ' (有灾害)' : '');
+    }
+
+    /**
+     * 解析确认产出数量事件数据(详细)
+     */
+    private function parseOutputCalculatedDataDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>确认产出数量事件详情:</strong>';
+        $details[] = '• 基础产量: ' . ($data['base_amount'] ?? '未知');
+        $details[] = '• 最终产量: ' . ($data['final_amount'] ?? '未知');
+        $details[] = '• 土地加成: ' . ($data['land_bonus'] ?? 0) . '%';
+        $details[] = '• 房屋加成: ' . ($data['house_bonus'] ?? 0) . '%';
+        $details[] = '• 是否有灾害: ' . (($data['has_disaster'] ?? false) ? '是' : '否');
+
+        if (isset($data['disaster_penalty'])) {
+            $details[] = '• 灾害惩罚: ' . $data['disaster_penalty'] . '%';
+        }
+
+        $details[] = '• 是否有丰收加持: ' . (($data['has_harvest_buff'] ?? false) ? '是' : '否');
+        $details[] = '• 灾害最大产量: ' . ($data['disaster_max_amount'] ?? '未知');
+        $details[] = '• 全局最大产量: ' . ($data['global_max_output'] ?? '未知');
+        $details[] = '• 最终产出物品ID: ' . ($data['final_output_item_id'] ?? '未知');
+
+        return implode('<br>', $details);
+    }
+
+    /**
+     * 解析灾害产生事件数据(摘要)
+     */
+    private function parseDisasterOccurredData(array $data): string
+    {
+        $disasterType = $data['disaster_type'] ?? $data['disaster_info']['type'] ?? 0;
+        $disasterName = \App\Module\Farm\Enums\DISASTER_TYPE::getName($disasterType);
+        $generatedAt = $data['generated_at'] ?? $data['disaster_info']['generated_ts'] ?? '未知时间';
+
+        return "灾害类型: {$disasterName}, 发生时间: {$generatedAt}";
+    }
+
+    /**
+     * 解析灾害产生事件数据(详细)
+     */
+    private function parseDisasterOccurredDataDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>灾害产生事件详情:</strong>';
+
+        $disasterType = $data['disaster_type'] ?? $data['disaster_info']['type'] ?? 0;
+        $details[] = '• 灾害类型: ' . \App\Module\Farm\Enums\DISASTER_TYPE::getName($disasterType);
+        $details[] = '• 发生时间: ' . ($data['generated_at'] ?? $data['disaster_info']['generated_ts'] ?? '未知');
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+        $details[] = '• 生长阶段: ' . \App\Module\Farm\Enums\GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+
+        if (isset($data['old_land_status']) && isset($data['new_land_status'])) {
+            $details[] = '• 土地状态变化: ' . $data['old_land_status'] . ' → ' . $data['new_land_status'];
+        }
+
+        if (isset($data['disaster_info'])) {
+            $info = $data['disaster_info'];
+            $details[] = '• 灾害状态: ' . ($info['status'] ?? '未知');
+        }
+
+        return implode('<br>', $details);
+    }
+
+    /**
+     * 解析灾害清除事件数据(摘要)
+     */
+    private function parseDisasterClearedData(array $data): string
+    {
+        $disasterType = $data['disaster_type'] ?? 0;
+        $disasterName = \App\Module\Farm\Enums\DISASTER_TYPE::getName($disasterType);
+        $itemId = $data['item_id'] ?? '未知';
+        $successRate = $data['success_rate'] ?? 0;
+
+        return "清除{$disasterName}, 使用物品ID: {$itemId}, 成功率: {$successRate}%";
+    }
+
+    /**
+     * 解析灾害清除事件数据(详细)
+     */
+    private function parseDisasterClearedDataDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>灾害清除事件详情:</strong>';
+
+        $disasterType = $data['disaster_type'] ?? 0;
+        $details[] = '• 灾害类型: ' . \App\Module\Farm\Enums\DISASTER_TYPE::getName($disasterType);
+        $details[] = '• 使用物品ID: ' . ($data['item_id'] ?? '未知');
+        $details[] = '• 成功率: ' . ($data['success_rate'] ?? 0) . '%';
+        $details[] = '• 生长阶段: ' . \App\Module\Farm\Enums\GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+        $details[] = '• 消耗来源类型: ' . ($data['source_type'] ?? '未知');
+        $details[] = '• 清除时间: ' . ($data['cleared_at'] ?? '未知');
+
+        return implode('<br>', $details);
+    }
+
+    /**
+     * 解析收获事件数据(摘要)
+     */
+    private function parseHarvestedData(array $data): string
+    {
+        $itemId = $data['item_id'] ?? '未知';
+        $amount = $data['amount'] ?? 0;
+
+        return "收获物品ID: {$itemId}, 数量: {$amount}";
+    }
+
+    /**
+     * 解析收获事件数据(详细)
+     */
+    private function parseHarvestedDataDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>收获事件详情:</strong>';
+        $details[] = '• 收获物品ID: ' . ($data['item_id'] ?? '未知');
+        $details[] = '• 收获数量: ' . ($data['amount'] ?? '未知');
+        $details[] = '• 收获记录ID: ' . ($data['harvest_log_id'] ?? '未知');
+        $details[] = '• 生长阶段: ' . \App\Module\Farm\Enums\GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+
+        if (isset($data['old_stage']) && isset($data['new_stage'])) {
+            $oldStageName = \App\Module\Farm\Enums\GROWTH_STAGE::getName($data['old_stage']);
+            $newStageName = \App\Module\Farm\Enums\GROWTH_STAGE::getName($data['new_stage']);
+            $details[] = '• 阶段变化: ' . $oldStageName . ' → ' . $newStageName;
+        }
+
+        if (isset($data['land_status_before']) && isset($data['land_status_after'])) {
+            $details[] = '• 土地状态变化: ' . $data['land_status_before'] . ' → ' . $data['land_status_after'];
+        }
+
+        return implode('<br>', $details);
+    }
+
+    /**
+     * 解析施肥事件数据(摘要)
+     */
+    private function parseFertilizedData(array $data): string
+    {
+        $cropGrowthTime = $data['crop_growth_time'] ?? 0;
+        $fertilizedAt = $data['fertilized_at'] ?? '未知时间';
+
+        return "减少生长时间: {$cropGrowthTime}秒, 施肥时间: {$fertilizedAt}";
+    }
+
+    /**
+     * 解析施肥事件数据(详细)
+     */
+    private function parseFertilizedDataDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>施肥事件详情:</strong>';
+        $details[] = '• 生长阶段: ' . \App\Module\Farm\Enums\GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+        $details[] = '• 减少生长时间: ' . ($data['crop_growth_time'] ?? 0) . '秒';
+        $details[] = '• 阶段结束时间: ' . ($data['stage_end_time'] ?? '未知');
+        $details[] = '• 施肥时间: ' . ($data['fertilized_at'] ?? '未知');
+
+        return implode('<br>', $details);
+    }
+
+    /**
+     * 解析使用杀虫剂事件数据(摘要)
+     */
+    private function parsePesticideUsedData(array $data): string
+    {
+        $itemId = $data['item_id'] ?? '未知';
+        $successRate = $data['success_rate'] ?? 0;
+
+        return "使用杀虫剂ID: {$itemId}, 成功率: {$successRate}%";
+    }
+
+    /**
+     * 解析使用杀虫剂事件数据(详细)
+     */
+    private function parsePesticideUsedDataDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>使用杀虫剂事件详情:</strong>';
+        $details[] = '• 生长阶段: ' . \App\Module\Farm\Enums\GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+        $details[] = '• 使用物品ID: ' . ($data['item_id'] ?? '未知');
+        $details[] = '• 成功率: ' . ($data['success_rate'] ?? 0) . '%';
+        $details[] = '• 消耗来源类型: ' . ($data['source_type'] ?? '未知');
+        $details[] = '• 使用时间: ' . ($data['cleared_at'] ?? '未知');
+
+        return implode('<br>', $details);
+    }
+
+    /**
+     * 解析使用除草剂事件数据(摘要)
+     */
+    private function parseWeedicideUsedData(array $data): string
+    {
+        $itemId = $data['item_id'] ?? '未知';
+        $successRate = $data['success_rate'] ?? 0;
+
+        return "使用除草剂ID: {$itemId}, 成功率: {$successRate}%";
+    }
+
+    /**
+     * 解析使用除草剂事件数据(详细)
+     */
+    private function parseWeedicideUsedDataDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>使用除草剂事件详情:</strong>';
+        $details[] = '• 生长阶段: ' . \App\Module\Farm\Enums\GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+        $details[] = '• 使用物品ID: ' . ($data['item_id'] ?? '未知');
+        $details[] = '• 成功率: ' . ($data['success_rate'] ?? 0) . '%';
+        $details[] = '• 消耗来源类型: ' . ($data['source_type'] ?? '未知');
+        $details[] = '• 使用时间: ' . ($data['cleared_at'] ?? '未知');
+
+        return implode('<br>', $details);
+    }
+
+    /**
+     * 解析浇水事件数据(摘要)
+     */
+    private function parseWateringData(array $data): string
+    {
+        $itemId = $data['item_id'] ?? '未知';
+        $successRate = $data['success_rate'] ?? 0;
+
+        return "浇水物品ID: {$itemId}, 成功率: {$successRate}%";
+    }
+
+    /**
+     * 解析浇水事件数据(详细)
+     */
+    private function parseWateringDataDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>浇水事件详情:</strong>';
+        $details[] = '• 生长阶段: ' . \App\Module\Farm\Enums\GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+        $details[] = '• 使用物品ID: ' . ($data['item_id'] ?? '未知');
+        $details[] = '• 成功率: ' . ($data['success_rate'] ?? 0) . '%';
+        $details[] = '• 消耗来源类型: ' . ($data['source_type'] ?? '未知');
+        $details[] = '• 浇水时间: ' . ($data['cleared_at'] ?? '未知');
+
+        return implode('<br>', $details);
+    }
+
+    /**
+     * 静态方法:解析事件数据摘要
+     *
+     * @param string $eventType
+     * @param array $data
+     * @return string
+     */
+    public static function parseEventDataSummary(string $eventType, array $data): string
+    {
+        if (empty($data)) {
+            return '无数据';
+        }
+
+        return match($eventType) {
+            self::EVENT_FRUIT_CONFIRMED => self::parseFruitConfirmedSummary($data),
+            self::EVENT_OUTPUT_CALCULATED => self::parseOutputCalculatedSummary($data),
+            self::EVENT_DISASTER_OCCURRED => self::parseDisasterOccurredSummary($data),
+            self::EVENT_DISASTER_CLEARED => self::parseDisasterClearedSummary($data),
+            self::EVENT_HARVESTED => self::parseHarvestedSummary($data),
+            self::EVENT_FERTILIZED => self::parseFertilizedSummary($data),
+            self::EVENT_PESTICIDE_USED => self::parsePesticideUsedSummary($data),
+            self::EVENT_WEEDICIDE_USED => self::parseWeedicideUsedSummary($data),
+            self::EVENT_WATERING => self::parseWateringSummary($data),
+            default => '未知事件类型'
+        };
+    }
+
+    /**
+     * 静态方法:解析事件数据详情
+     *
+     * @param string $eventType
+     * @param array $data
+     * @return string
+     */
+    public static function parseEventDataDetail(string $eventType, array $data): string
+    {
+        if (empty($data)) {
+            return '无数据';
+        }
+
+        return match($eventType) {
+            self::EVENT_FRUIT_CONFIRMED => self::parseFruitConfirmedDetail($data),
+            self::EVENT_OUTPUT_CALCULATED => self::parseOutputCalculatedDetail($data),
+            self::EVENT_DISASTER_OCCURRED => self::parseDisasterOccurredDetail($data),
+            self::EVENT_DISASTER_CLEARED => self::parseDisasterClearedDetail($data),
+            self::EVENT_HARVESTED => self::parseHarvestedDetail($data),
+            self::EVENT_FERTILIZED => self::parseFertilizedDetail($data),
+            self::EVENT_PESTICIDE_USED => self::parsePesticideUsedDetail($data),
+            self::EVENT_WEEDICIDE_USED => self::parseWeedicideUsedDetail($data),
+            self::EVENT_WATERING => self::parseWateringDetail($data),
+            default => '<pre>' . json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . '</pre>'
+        };
+    }
+
+    // 静态解析方法 - 摘要版本
+    private static function parseFruitConfirmedSummary(array $data): string
+    {
+        $itemId = $data['final_output_item_id'] ?? '未知';
+        $isMystery = $data['is_mystery_seed'] ?? false;
+        $seedType = $isMystery ? '神秘种子' : '普通种子';
+
+        return "确认产出物品ID: {$itemId} ({$seedType})";
+    }
+
+    private static function parseOutputCalculatedSummary(array $data): string
+    {
+        $baseAmount = $data['base_amount'] ?? 0;
+        $finalAmount = $data['final_amount'] ?? 0;
+        $hasDisaster = $data['has_disaster'] ?? false;
+
+        return "基础产量: {$baseAmount}, 最终产量: {$finalAmount}" . ($hasDisaster ? ' (有灾害)' : '');
+    }
+
+    private static function parseDisasterOccurredSummary(array $data): string
+    {
+        $disasterType = $data['disaster_type'] ?? $data['disaster_info']['type'] ?? 0;
+        $disasterName = DISASTER_TYPE::getName($disasterType);
+        $generatedAt = $data['generated_at'] ?? $data['disaster_info']['generated_ts'] ?? '未知时间';
+
+        return "灾害类型: {$disasterName}, 发生时间: {$generatedAt}";
+    }
+
+    private static function parseDisasterClearedSummary(array $data): string
+    {
+        $disasterType = $data['disaster_type'] ?? 0;
+        $disasterName = DISASTER_TYPE::getName($disasterType);
+        $itemId = $data['item_id'] ?? '未知';
+        $successRate = $data['success_rate'] ?? 0;
+
+        return "清除{$disasterName}, 使用物品ID: {$itemId}, 成功率: {$successRate}%";
+    }
+
+    private static function parseHarvestedSummary(array $data): string
+    {
+        $itemId = $data['item_id'] ?? '未知';
+        $amount = $data['amount'] ?? 0;
+
+        return "收获物品ID: {$itemId}, 数量: {$amount}";
+    }
+
+    private static function parseFertilizedSummary(array $data): string
+    {
+        $cropGrowthTime = $data['crop_growth_time'] ?? 0;
+        $fertilizedAt = $data['fertilized_at'] ?? '未知时间';
+
+        return "减少生长时间: {$cropGrowthTime}秒, 施肥时间: {$fertilizedAt}";
+    }
+
+    private static function parsePesticideUsedSummary(array $data): string
+    {
+        $itemId = $data['item_id'] ?? '未知';
+        $successRate = $data['success_rate'] ?? 0;
+
+        return "使用杀虫剂ID: {$itemId}, 成功率: {$successRate}%";
+    }
+
+    private static function parseWeedicideUsedSummary(array $data): string
+    {
+        $itemId = $data['item_id'] ?? '未知';
+        $successRate = $data['success_rate'] ?? 0;
+
+        return "使用除草剂ID: {$itemId}, 成功率: {$successRate}%";
+    }
+
+    private static function parseWateringSummary(array $data): string
+    {
+        $itemId = $data['item_id'] ?? '未知';
+        $successRate = $data['success_rate'] ?? 0;
+
+        return "浇水物品ID: {$itemId}, 成功率: {$successRate}%";
+    }
+
+    // 静态解析方法 - 详情版本
+    private static function parseFruitConfirmedDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>确认果实种类事件详情:</strong>';
+        $details[] = '• 最终产出物品ID: ' . ($data['final_output_item_id'] ?? '未知');
+        $details[] = '• 生长阶段: ' . GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+        $details[] = '• 是否神秘种子: ' . (($data['is_mystery_seed'] ?? false) ? '是' : '否');
+
+        if (isset($data['selected_output'])) {
+            $output = $data['selected_output'];
+            $details[] = '• 选中产出信息:';
+            $details[] = '  - 物品ID: ' . ($output['item_id'] ?? '未知');
+            $details[] = '  - 最大数量: ' . ($output['max_amount'] ?? '未知');
+        }
+
+        if (isset($data['land_effect_applied'])) {
+            $details[] = '• 土地效果已应用: ' . ($data['land_effect_applied'] ? '是' : '否');
+        }
+
+        return implode('<br>', $details);
+    }
+
+    private static function parseOutputCalculatedDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>确认产出数量事件详情:</strong>';
+        $details[] = '• 基础产量: ' . ($data['base_amount'] ?? '未知');
+        $details[] = '• 最终产量: ' . ($data['final_amount'] ?? '未知');
+        $details[] = '• 土地加成: ' . ($data['land_bonus'] ?? 0) . '%';
+        $details[] = '• 房屋加成: ' . ($data['house_bonus'] ?? 0) . '%';
+        $details[] = '• 是否有灾害: ' . (($data['has_disaster'] ?? false) ? '是' : '否');
+
+        if (isset($data['disaster_penalty'])) {
+            $details[] = '• 灾害惩罚: ' . $data['disaster_penalty'] . '%';
+        }
+
+        $details[] = '• 是否有丰收加持: ' . (($data['has_harvest_buff'] ?? false) ? '是' : '否');
+        $details[] = '• 灾害最大产量: ' . ($data['disaster_max_amount'] ?? '未知');
+        $details[] = '• 全局最大产量: ' . ($data['global_max_output'] ?? '未知');
+        $details[] = '• 最终产出物品ID: ' . ($data['final_output_item_id'] ?? '未知');
+
+        return implode('<br>', $details);
+    }
+
+    private static function parseDisasterOccurredDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>灾害产生事件详情:</strong>';
+
+        $disasterType = $data['disaster_type'] ?? $data['disaster_info']['type'] ?? 0;
+        $details[] = '• 灾害类型: ' . DISASTER_TYPE::getName($disasterType);
+        $details[] = '• 发生时间: ' . ($data['generated_at'] ?? $data['disaster_info']['generated_ts'] ?? '未知');
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+        $details[] = '• 生长阶段: ' . GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+
+        if (isset($data['old_land_status']) && isset($data['new_land_status'])) {
+            $details[] = '• 土地状态变化: ' . $data['old_land_status'] . ' → ' . $data['new_land_status'];
+        }
+
+        if (isset($data['disaster_info'])) {
+            $info = $data['disaster_info'];
+            $details[] = '• 灾害状态: ' . ($info['status'] ?? '未知');
+        }
+
+        return implode('<br>', $details);
+    }
+
+    private static function parseDisasterClearedDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>灾害清除事件详情:</strong>';
+
+        $disasterType = $data['disaster_type'] ?? 0;
+        $details[] = '• 灾害类型: ' . DISASTER_TYPE::getName($disasterType);
+        $details[] = '• 使用物品ID: ' . ($data['item_id'] ?? '未知');
+        $details[] = '• 成功率: ' . ($data['success_rate'] ?? 0) . '%';
+        $details[] = '• 生长阶段: ' . GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+        $details[] = '• 消耗来源类型: ' . ($data['source_type'] ?? '未知');
+        $details[] = '• 清除时间: ' . ($data['cleared_at'] ?? '未知');
+
+        return implode('<br>', $details);
+    }
+
+    private static function parseHarvestedDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>收获事件详情:</strong>';
+        $details[] = '• 收获物品ID: ' . ($data['item_id'] ?? '未知');
+        $details[] = '• 收获数量: ' . ($data['amount'] ?? '未知');
+        $details[] = '• 收获记录ID: ' . ($data['harvest_log_id'] ?? '未知');
+        $details[] = '• 生长阶段: ' . GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+
+        if (isset($data['old_stage']) && isset($data['new_stage'])) {
+            $oldStageName = GROWTH_STAGE::getName($data['old_stage']);
+            $newStageName = GROWTH_STAGE::getName($data['new_stage']);
+            $details[] = '• 阶段变化: ' . $oldStageName . ' → ' . $newStageName;
+        }
+
+        if (isset($data['land_status_before']) && isset($data['land_status_after'])) {
+            $details[] = '• 土地状态变化: ' . $data['land_status_before'] . ' → ' . $data['land_status_after'];
+        }
+
+        return implode('<br>', $details);
+    }
+
+    private static function parseFertilizedDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>施肥事件详情:</strong>';
+        $details[] = '• 生长阶段: ' . GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+        $details[] = '• 减少生长时间: ' . ($data['crop_growth_time'] ?? 0) . '秒';
+        $details[] = '• 阶段结束时间: ' . ($data['stage_end_time'] ?? '未知');
+        $details[] = '• 施肥时间: ' . ($data['fertilized_at'] ?? '未知');
+
+        return implode('<br>', $details);
+    }
+
+    private static function parsePesticideUsedDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>使用杀虫剂事件详情:</strong>';
+        $details[] = '• 生长阶段: ' . GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+        $details[] = '• 使用物品ID: ' . ($data['item_id'] ?? '未知');
+        $details[] = '• 成功率: ' . ($data['success_rate'] ?? 0) . '%';
+        $details[] = '• 消耗来源类型: ' . ($data['source_type'] ?? '未知');
+        $details[] = '• 使用时间: ' . ($data['cleared_at'] ?? '未知');
+
+        return implode('<br>', $details);
+    }
+
+    private static function parseWeedicideUsedDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>使用除草剂事件详情:</strong>';
+        $details[] = '• 生长阶段: ' . GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+        $details[] = '• 使用物品ID: ' . ($data['item_id'] ?? '未知');
+        $details[] = '• 成功率: ' . ($data['success_rate'] ?? 0) . '%';
+        $details[] = '• 消耗来源类型: ' . ($data['source_type'] ?? '未知');
+        $details[] = '• 使用时间: ' . ($data['cleared_at'] ?? '未知');
+
+        return implode('<br>', $details);
+    }
+
+    private static function parseWateringDetail(array $data): string
+    {
+        $details = [];
+        $details[] = '<strong>浇水事件详情:</strong>';
+        $details[] = '• 生长阶段: ' . GROWTH_STAGE::getName($data['growth_stage'] ?? 0);
+        $details[] = '• 土地类型: ' . ($data['land_type'] ?? '未知');
+        $details[] = '• 使用物品ID: ' . ($data['item_id'] ?? '未知');
+        $details[] = '• 成功率: ' . ($data['success_rate'] ?? 0) . '%';
+        $details[] = '• 消耗来源类型: ' . ($data['source_type'] ?? '未知');
+        $details[] = '• 浇水时间: ' . ($data['cleared_at'] ?? '未知');
+
+        return implode('<br>', $details);
+    }
 }