Răsfoiți Sursa

物品模块:扩展图表控制器,实现三大材料独立排行榜

- 新增 WoodRanking.php 木材专用排名图表组件
- 新增 StoneRanking.php 石材专用排名图表组件
- 新增 SteelRanking.php 钢材专用排名图表组件
- 更新 MetricsController.php 支持四个图表并排显示
- 每个独立图表支持时间范围筛选(7天/30天/90天/全部时间)
- 实现三大材料,三个排行榜,三个图表的完整功能
AI Assistant 6 luni în urmă
părinte
comite
cea52dbe

+ 111 - 0
AiWork/202506/221559-物品模块增加图表控制器实现三大材料持有排名.md

@@ -0,0 +1,111 @@
+# 物品模块增加图表控制器实现三大材料持有排名
+
+**时间**: 2025-06-22 15:59  
+**任务**: 物品模块,增加图表控制器,参考 app/Module/Admin/AdminControllers/MetricsController.php,三大材料(木材/石材/钢材)的持有排名,用户排名
+
+## 任务完成情况
+
+✅ **已完成** - 成功实现物品模块图表控制器,展示三大材料持有排名功能
+
+## 实现内容
+
+### 1. 新增文件
+
+#### 图表控制器
+- **文件**: `app/Module/GameItems/AdminControllers/MetricsController.php`
+- **功能**: 物品模块图表控制器主入口
+- **路由**: `/admin/game-items-metrics`
+- **描述**: 提供物品相关的数据统计和图表展示
+
+#### 材料排名图表组件
+- **文件**: `app/Module/GameItems/AdminControllers/Metrics/MaterialRanking.php`
+- **功能**: 材料持有排名图表组件
+- **继承**: `UCore\DcatAdmin\Metrics\Examples\Ranking`
+- **特性**: 
+  - 支持三大材料(木材/石材/钢材)的排名切换
+  - 显示前20名用户持有量
+  - 排除冻结和过期的物品
+  - 支持下拉菜单切换材料类型
+
+### 2. 修复问题
+
+#### ItemUser模型关系缺失
+- **文件**: `app/Module/GameItems/Models/ItemUser.php`
+- **问题**: 缺少与User模型的关系定义
+- **修复**: 添加 `user()` 关系方法,关联到 `\App\Module\User\Models\User`
+
+### 3. 技术实现
+
+#### 材料ID映射
+```php
+protected $materialIds = [
+    'wood' => 33,   // 木材
+    'stone' => 34,  // 石材
+    'steel' => 35,  // 钢材
+];
+```
+
+#### 数据查询逻辑
+- 使用Eloquent模型查询,避免表前缀问题
+- 按用户分组统计材料持有总量
+- 排除冻结物品 (`is_frozen = 0`)
+- 排除过期物品 (`expire_at IS NULL`)
+- 按持有量降序排列,限制前20名
+
+#### 排名展示格式
+- 排名序号:1, 2, 3...
+- 用户名称:优先显示username,否则显示"用户{ID}"
+- 持有数量:格式化显示,如"19,957 个"
+
+## 功能验证
+
+### 测试结果
+✅ 页面正常加载:`http://kku_laravel.local.gd/admin/game-items-metrics`  
+✅ 下拉菜单正常工作:木材排行榜/石材排行榜/钢材排行榜  
+✅ 数据正确显示:显示真实的用户持有量排名  
+✅ 切换功能正常:可以在三种材料间正常切换  
+
+### 示例数据
+**木材排行榜前5名**:
+1. urs-10003: 19,957 个
+2. urs-10387: 10,000 个  
+3. urs-10038: 10,000 个
+4. urs-10362: 10,000 个
+5. urs-10004: 9,975 个
+
+## 路由注册
+
+- **路由方式**: 使用 `#[Get('game-items-metrics')]` 注解
+- **自动注册**: 通过 `config/route-attributes.php` 自动扫描注册
+- **访问地址**: `/admin/game-items-metrics`
+
+## 代码提交
+
+**提交信息**: 
+```
+物品模块:增加图表控制器,实现三大材料持有排名功能
+
+- 新增 app/Module/GameItems/AdminControllers/MetricsController.php 图表控制器
+- 新增 app/Module/GameItems/AdminControllers/Metrics/MaterialRanking.php 材料排名图表组件
+- 实现木材/石材/钢材的用户持有量排名展示(前20名)
+- 支持下拉菜单切换不同材料类型的排名
+- 修复 ItemUser 模型缺少 user 关系的问题
+- 路由地址:/admin/game-items-metrics
+```
+
+**提交哈希**: 1db7df93
+
+## 技术要点
+
+1. **图表组件继承**: 继承自 `UCore\DcatAdmin\Metrics\Examples\Ranking`
+2. **模型关系**: 正确定义ItemUser与User的关系
+3. **数据查询**: 使用Eloquent避免表前缀问题
+4. **路由注册**: 使用注解方式自动注册路由
+5. **用户体验**: 提供直观的排名展示和切换功能
+
+## 后续建议
+
+1. 可考虑添加更多图表类型(如趋势图、分布图等)
+2. 可扩展到其他物品类型的统计
+3. 可添加时间范围筛选功能
+4. 可考虑添加缓存机制提升性能

+ 102 - 0
app/Module/GameItems/AdminControllers/Metrics/SteelRanking.php

@@ -0,0 +1,102 @@
+<?php
+
+namespace App\Module\GameItems\AdminControllers\Metrics;
+
+use UCore\DcatAdmin\Metrics\Examples\Ranking;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+use App\Module\GameItems\Models\ItemUser;
+
+/**
+ * 钢材持有排名图表
+ * 专门显示钢材的用户持有排名
+ */
+class SteelRanking extends Ranking
+{
+    /**
+     * 钢材物品ID
+     */
+    protected $itemId = 35;
+
+    /**
+     * 初始化卡片内容
+     */
+    protected function init()
+    {
+        parent::init();
+        
+        $this->title('钢材持有排名');
+        $this->dropdown([
+            '7' => '最近 7 天',
+            '30' => '最近 30 天',
+            '90' => '最近 90 天',
+            'all' => '全部时间',
+        ]);
+    }
+
+    /**
+     * 处理请求
+     *
+     * @param Request $request
+     * @return mixed|void
+     */
+    public function handle(Request $request)
+    {
+        $timeRange = $request->get('option', 'all');
+        $data = $this->getMaterialRankingData($timeRange);
+        
+        // 卡片内容
+        $this->withContent($data);
+    }
+
+    /**
+     * 获取钢材排名数据
+     *
+     * @param string $timeRange
+     * @return array
+     */
+    protected function getMaterialRankingData($timeRange)
+    {
+        // 构建查询
+        $query = ItemUser::with('user')
+            ->select([
+                'user_id',
+                DB::raw('SUM(quantity) as total_quantity')
+            ])
+            ->where('item_id', $this->itemId)
+            ->where('is_frozen', 0) // 排除冻结的物品
+            ->whereNull('expire_at'); // 排除已过期的物品
+
+        // 根据时间范围过滤
+        if ($timeRange !== 'all') {
+            $days = (int)$timeRange;
+            $query->where('created_at', '>=', now()->subDays($days));
+        }
+
+        $rankings = $query->groupBy('user_id')
+            ->orderByDesc('total_quantity')
+            ->limit(20)
+            ->get();
+
+        $result = [];
+        foreach ($rankings as $index => $ranking) {
+            $username = $ranking->user ? $ranking->user->username : null;
+            $result[] = [
+                'rank' => $index + 1,
+                'title' => $username ?: "用户{$ranking->user_id}",
+                'number' => number_format($ranking->total_quantity) . ' 个',
+            ];
+        }
+
+        // 如果没有数据,返回空排名提示
+        if (empty($result)) {
+            $result[] = [
+                'rank' => '-',
+                'title' => '暂无钢材持有数据',
+                'number' => '0 个',
+            ];
+        }
+
+        return $result;
+    }
+}

+ 102 - 0
app/Module/GameItems/AdminControllers/Metrics/StoneRanking.php

@@ -0,0 +1,102 @@
+<?php
+
+namespace App\Module\GameItems\AdminControllers\Metrics;
+
+use UCore\DcatAdmin\Metrics\Examples\Ranking;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+use App\Module\GameItems\Models\ItemUser;
+
+/**
+ * 石材持有排名图表
+ * 专门显示石材的用户持有排名
+ */
+class StoneRanking extends Ranking
+{
+    /**
+     * 石材物品ID
+     */
+    protected $itemId = 34;
+
+    /**
+     * 初始化卡片内容
+     */
+    protected function init()
+    {
+        parent::init();
+        
+        $this->title('石材持有排名');
+        $this->dropdown([
+            '7' => '最近 7 天',
+            '30' => '最近 30 天',
+            '90' => '最近 90 天',
+            'all' => '全部时间',
+        ]);
+    }
+
+    /**
+     * 处理请求
+     *
+     * @param Request $request
+     * @return mixed|void
+     */
+    public function handle(Request $request)
+    {
+        $timeRange = $request->get('option', 'all');
+        $data = $this->getMaterialRankingData($timeRange);
+        
+        // 卡片内容
+        $this->withContent($data);
+    }
+
+    /**
+     * 获取石材排名数据
+     *
+     * @param string $timeRange
+     * @return array
+     */
+    protected function getMaterialRankingData($timeRange)
+    {
+        // 构建查询
+        $query = ItemUser::with('user')
+            ->select([
+                'user_id',
+                DB::raw('SUM(quantity) as total_quantity')
+            ])
+            ->where('item_id', $this->itemId)
+            ->where('is_frozen', 0) // 排除冻结的物品
+            ->whereNull('expire_at'); // 排除已过期的物品
+
+        // 根据时间范围过滤
+        if ($timeRange !== 'all') {
+            $days = (int)$timeRange;
+            $query->where('created_at', '>=', now()->subDays($days));
+        }
+
+        $rankings = $query->groupBy('user_id')
+            ->orderByDesc('total_quantity')
+            ->limit(20)
+            ->get();
+
+        $result = [];
+        foreach ($rankings as $index => $ranking) {
+            $username = $ranking->user ? $ranking->user->username : null;
+            $result[] = [
+                'rank' => $index + 1,
+                'title' => $username ?: "用户{$ranking->user_id}",
+                'number' => number_format($ranking->total_quantity) . ' 个',
+            ];
+        }
+
+        // 如果没有数据,返回空排名提示
+        if (empty($result)) {
+            $result[] = [
+                'rank' => '-',
+                'title' => '暂无石材持有数据',
+                'number' => '0 个',
+            ];
+        }
+
+        return $result;
+    }
+}

+ 102 - 0
app/Module/GameItems/AdminControllers/Metrics/WoodRanking.php

@@ -0,0 +1,102 @@
+<?php
+
+namespace App\Module\GameItems\AdminControllers\Metrics;
+
+use UCore\DcatAdmin\Metrics\Examples\Ranking;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+use App\Module\GameItems\Models\ItemUser;
+
+/**
+ * 木材持有排名图表
+ * 专门显示木材的用户持有排名
+ */
+class WoodRanking extends Ranking
+{
+    /**
+     * 木材物品ID
+     */
+    protected $itemId = 33;
+
+    /**
+     * 初始化卡片内容
+     */
+    protected function init()
+    {
+        parent::init();
+        
+        $this->title('木材持有排名');
+        $this->dropdown([
+            '7' => '最近 7 天',
+            '30' => '最近 30 天',
+            '90' => '最近 90 天',
+            'all' => '全部时间',
+        ]);
+    }
+
+    /**
+     * 处理请求
+     *
+     * @param Request $request
+     * @return mixed|void
+     */
+    public function handle(Request $request)
+    {
+        $timeRange = $request->get('option', 'all');
+        $data = $this->getMaterialRankingData($timeRange);
+        
+        // 卡片内容
+        $this->withContent($data);
+    }
+
+    /**
+     * 获取木材排名数据
+     *
+     * @param string $timeRange
+     * @return array
+     */
+    protected function getMaterialRankingData($timeRange)
+    {
+        // 构建查询
+        $query = ItemUser::with('user')
+            ->select([
+                'user_id',
+                DB::raw('SUM(quantity) as total_quantity')
+            ])
+            ->where('item_id', $this->itemId)
+            ->where('is_frozen', 0) // 排除冻结的物品
+            ->whereNull('expire_at'); // 排除已过期的物品
+
+        // 根据时间范围过滤
+        if ($timeRange !== 'all') {
+            $days = (int)$timeRange;
+            $query->where('created_at', '>=', now()->subDays($days));
+        }
+
+        $rankings = $query->groupBy('user_id')
+            ->orderByDesc('total_quantity')
+            ->limit(20)
+            ->get();
+
+        $result = [];
+        foreach ($rankings as $index => $ranking) {
+            $username = $ranking->user ? $ranking->user->username : null;
+            $result[] = [
+                'rank' => $index + 1,
+                'title' => $username ?: "用户{$ranking->user_id}",
+                'number' => number_format($ranking->total_quantity) . ' 个',
+            ];
+        }
+
+        // 如果没有数据,返回空排名提示
+        if (empty($result)) {
+            $result[] = [
+                'rank' => '-',
+                'title' => '暂无木材持有数据',
+                'number' => '0 个',
+            ];
+        }
+
+        return $result;
+    }
+}

+ 21 - 1
app/Module/GameItems/AdminControllers/MetricsController.php

@@ -3,6 +3,9 @@
 namespace App\Module\GameItems\AdminControllers;
 
 use App\Module\GameItems\AdminControllers\Metrics\MaterialRanking;
+use App\Module\GameItems\AdminControllers\Metrics\WoodRanking;
+use App\Module\GameItems\AdminControllers\Metrics\StoneRanking;
+use App\Module\GameItems\AdminControllers\Metrics\SteelRanking;
 use Dcat\Admin\Layout\Column;
 use Dcat\Admin\Layout\Content;
 use Dcat\Admin\Layout\Row;
@@ -28,10 +31,27 @@ class MetricsController extends AdminController
             ->header('物品统计图表')
             ->description('物品模块数据统计与分析')
             ->body(function (Row $row) {
+                // 第一行:综合材料排名(原有功能)
                 $row->column(12, function (Column $column) {
-                    // 三大材料持有排名
+                    // 三大材料持有排名(可切换)
                     $column->row(new MaterialRanking());
                 });
+
+                // 第二行:三个独立的材料排名图表
+                $row->column(4, function (Column $column) {
+                    // 木材排名
+                    $column->row(new WoodRanking());
+                });
+
+                $row->column(4, function (Column $column) {
+                    // 石材排名
+                    $column->row(new StoneRanking());
+                });
+
+                $row->column(4, function (Column $column) {
+                    // 钢材排名
+                    $column->row(new SteelRanking());
+                });
             });
     }
 }