Explorar o código

Mex模块挂单增加'最后无法成交原因'字段,后台可见

功能概述:
- 为农贸市场挂单增加last_match_failure_reason字段
- 在后台订单管理页面显示无法成交的具体原因
- 在撮合过程中记录各种无法成交的情况

数据库变更:
- 在kku_mex_orders表增加last_match_failure_reason字段
- 字段类型为text,用于存储详细的无法成交原因

模型更新:
- 更新MexOrder模型,添加新字段的属性定义
- 将新字段加入fillable数组

后台显示:
- 在订单列表页面增加'最后无法成交原因'列
- 在订单详情页面显示完整的无法成交原因
- 在订单编辑页面显示无法成交原因字段
- 列表页面限制显示50个字符,详情页面显示完整内容

撮合逻辑增强:
- 价格验证失败时记录具体原因
- 库存不足时记录当前库存和需求数量
- 撮合执行失败时记录详细错误信息
- 成功撮合后清除之前的无法成交原因
- 支持买入和卖出订单的无法成交原因记录

错误修复:
- 修复GameItems模块logTransaction方法的类型错误
- 支持枚举类型的sourceType参数处理

测试验证:
- 订单113显示物品数量不足的详细原因
- 订单118成功撮合后清除无法成交原因
- 后台页面正常显示无法成交原因信息

用户价值:
- 管理员可以清楚了解订单无法成交的具体原因
- 便于排查撮合问题和优化系统配置
- 提供详细的错误信息用于问题诊断
AI Assistant hai 6 meses
pai
achega
f5f76e76dc

+ 268 - 0
AiWork/2025年06月/21日1847-Mex模块仓库优化增加资金仓库显示功能.md

@@ -0,0 +1,268 @@
+# Mex模块仓库优化:增加资金仓库显示功能
+
+**任务时间**: 2025年06月21日 18:47  
+**任务类型**: 功能增强  
+**模块**: Mex/仓库管理  
+
+## 任务概述
+
+为农贸市场仓库管理页面增加资金仓库显示功能,让管理员可以方便地查看仓库账户的资金状况。通过标签页设计,用户可以在物品仓库和资金仓库之间切换查看。
+
+## 需求分析
+
+### 原有功能
+- 仅显示物品仓库信息
+- 包含商品库存、买入卖出数量、金额统计等
+
+### 新增需求
+- 增加资金仓库显示
+- 显示仓库账户的各类资金余额
+- 支持资金类型识别和格式化显示
+- 保持与物品仓库一致的操作体验
+
+## 技术实现
+
+### 1. 页面结构改造
+
+#### 使用Tab组件实现标签页
+```php
+protected function grid()
+{
+    $tab = new Tab();
+    
+    // 物品仓库标签页
+    $tab->add('物品仓库', $this->itemWarehouseGrid());
+    
+    // 资金仓库标签页
+    $tab->add('资金仓库', $this->fundWarehouseGrid());
+    
+    return $tab;
+}
+```
+
+#### 原有物品仓库逻辑重构
+- 将原有grid()方法内容提取到itemWarehouseGrid()方法
+- 保持原有功能不变
+
+### 2. 资金仓库功能实现
+
+#### 核心方法:fundWarehouseGrid()
+```php
+protected function fundWarehouseGrid()
+{
+    // 获取仓库账户ID
+    $warehouseUserId = config('mex.accounts.warehouse_user_id', 15);
+    
+    return Grid::make(FundModel::class, function (Grid $grid) use ($warehouseUserId) {
+        // 只显示仓库账户的资金
+        $grid->model()->where('user_id', $warehouseUserId);
+        
+        // 配置显示列
+        $grid->column('fund_id', '资金类型')->display(function ($value) {
+            return self::getFundTypeName($value);
+        });
+        // ... 其他列配置
+    });
+}
+```
+
+#### 资金类型识别
+```php
+private static function getFundTypeName($fundId): string
+{
+    // 支持枚举类型和int类型参数
+    if ($fundId instanceof FUND_TYPE) {
+        $fundType = $fundId;
+    } else {
+        $fundType = FUND_TYPE::tryFrom((int)$fundId);
+    }
+    
+    return match($fundType) {
+        FUND_TYPE::FUND1 => '金币账户',
+        FUND_TYPE::FUND2 => '钻石账户',
+        FUND_TYPE::FUND3 => '钻石冻结账户',
+        default => "未知类型({$fundId})"
+    };
+}
+```
+
+### 3. 显示列配置
+
+#### 主要显示字段
+1. **ID**: 资金记录ID
+2. **资金类型**: 金币账户/钻石账户/钻石冻结账户
+3. **当前余额**: 根据币种精度格式化显示
+4. **币种**: 金币/钻石
+5. **格式化余额**: 余额+币种单位
+6. **更新时间**: 最后更新时间
+7. **创建时间**: 账户创建时间
+
+#### 精度处理
+```php
+private static function getFundPrecision($fundId): int
+{
+    $fundType = FUND_TYPE::tryFrom((int)$fundId);
+    if (!$fundType) {
+        return 2;
+    }
+    
+    $currency = FUND_TYPE::type2Currency($fundType);
+    return $currency ? $currency->getPrecision() : 2;
+}
+```
+
+### 4. 功能特性
+
+#### 筛选功能
+- 按资金类型筛选(金币账户/钻石账户/钻石冻结账户)
+- 按余额范围筛选
+- 按更新时间范围筛选
+
+#### 工具栏
+- 刷新按钮
+- 筛选按钮  
+- 资金统计按钮
+
+#### 操作按钮
+- 显示:查看详细信息
+- 详情:查看资金详情
+
+## 测试验证
+
+### 1. 页面访问测试
+- URL: http://kku_laravel.local.gd/admin/mex-warehouse
+- 页面正常加载,显示两个标签页
+
+### 2. 资金仓库显示测试
+
+#### 显示数据验证
+```
+金币账户:
+- ID: 90481
+- 余额: 9,400,000,000,000 金币
+- 更新时间: 2024-10-09 17:38:59
+
+钻石账户:
+- ID: 90482  
+- 余额: 91,001,528,611.0000000000 钻石
+- 更新时间: 2025-06-21 18:39:54
+
+钻石冻结账户:
+- ID: 337225
+- 余额: 0.0000000000 钻石
+- 更新时间: 2025-06-18 22:22:49
+```
+
+#### 功能测试
+- ✅ 标签页切换正常
+- ✅ 资金类型显示正确
+- ✅ 余额格式化正确
+- ✅ 币种显示正确
+- ✅ 时间显示正确
+- ✅ 筛选功能可用
+- ✅ 操作按钮可用
+
+### 3. 兼容性测试
+- ✅ 物品仓库功能保持不变
+- ✅ 原有操作流程不受影响
+- ✅ 页面样式保持一致
+
+## 技术亮点
+
+### 1. 灵活的参数处理
+- 支持枚举类型和int类型的fund_id参数
+- 避免了类型转换错误
+
+### 2. 精确的精度控制
+- 根据币种类型自动获取显示精度
+- 金币显示为整数,钻石显示为10位小数
+
+### 3. 一致的用户体验
+- 与物品仓库保持相同的操作界面
+- 统一的筛选、排序、分页功能
+
+### 4. 可扩展的设计
+- 易于添加新的资金类型
+- 支持配置仓库账户ID
+
+## 代码结构
+
+### 修改文件
+- `app/Module/Mex/AdminControllers/MexWarehouseController.php`
+
+### 新增方法
+1. `fundWarehouseGrid()` - 资金仓库Grid配置
+2. `getFundTypeName()` - 获取资金类型名称
+3. `getCurrencyName()` - 获取币种名称  
+4. `getFundPrecision()` - 获取资金精度
+
+### 重构方法
+1. `grid()` - 改为返回Tab组件
+2. `itemWarehouseGrid()` - 提取原有物品仓库逻辑
+
+## 用户价值
+
+### 1. 管理便利性
+- 一个页面查看物品和资金两类仓库信息
+- 清晰了解仓库资金状况
+
+### 2. 监控能力
+- 实时查看各类资金余额
+- 监控资金变动时间
+
+### 3. 操作效率
+- 标签页快速切换
+- 统一的操作界面
+
+## 后续优化建议
+
+### 1. 功能增强
+- 添加资金变动趋势图表
+- 增加资金预警功能
+- 支持资金操作日志查看
+
+### 2. 性能优化
+- 考虑大数据量时的分页优化
+- 添加缓存机制
+
+### 3. 用户体验
+- 添加资金统计汇总信息
+- 支持导出资金报表
+
+## 提交信息
+
+```
+Mex模块仓库优化:增加资金仓库显示功能
+
+功能概述:
+- 在农贸市场仓库管理页面增加资金仓库标签页
+- 用户可以在物品仓库和资金仓库之间切换查看
+
+技术实现:
+- 使用Dcat Admin的Tab组件实现标签页切换
+- 创建fundWarehouseGrid()方法显示仓库账户的资金信息
+- 支持显示金币账户、钻石账户、钻石冻结账户等资金类型
+- 添加资金类型名称、币种名称、精度等辅助方法
+
+显示内容:
+- 资金类型(金币账户/钻石账户/钻石冻结账户)
+- 当前余额(根据币种精度格式化显示)
+- 币种名称(金币/钻石)
+- 格式化余额(余额+币种单位)
+- 更新时间和创建时间
+
+功能特点:
+- 支持按资金类型、余额范围等条件筛选
+- 提供资金统计工具按钮
+- 支持查看详情和显示操作
+- 自动获取仓库账户ID(默认用户ID 15)
+
+用户体验:
+- 标签页设计便于在物品仓库和资金仓库间切换
+- 清晰显示各类资金的余额和状态
+- 便于管理员监控仓库资金情况
+```
+
+## 总结
+
+成功为Mex模块的仓库管理页面增加了资金仓库显示功能,通过标签页设计实现了物品仓库和资金仓库的统一管理界面。功能实现完整,用户体验良好,为管理员提供了更全面的仓库监控能力。

+ 18 - 3
app/Module/GameItems/Logics/Item.php

@@ -505,7 +505,7 @@ class Item
      * @param int|null $instanceId 物品实例ID
      * @param int $quantity 数量
      * @param int $transactionType 交易类型
-     * @param string|null $sourceType 来源类型
+     * @param mixed $sourceType 来源类型(支持字符串或枚举类型)
      * @param int|null $sourceId 来源ID
      * @param array|null $details 详细信息
      * @param string|null $expireAt 过期时间
@@ -519,7 +519,7 @@ class Item
         ?int    $instanceId,
         int     $quantity,
         int     $transactionType,
-        ?string $sourceType = null,
+        $sourceType = null,
         ?int    $sourceId = null,
         ?array  $details = null,
         ?string $expireAt = null,
@@ -527,13 +527,28 @@ class Item
         ?string $deviceInfo = null
     ): ItemTransactionLog
     {
+        // 处理枚举类型的sourceType
+        $sourceTypeValue = null;
+        if ($sourceType !== null) {
+            if (is_object($sourceType) && method_exists($sourceType, 'value')) {
+                // 如果是枚举类型,获取其值
+                $sourceTypeValue = $sourceType->value;
+            } elseif (is_string($sourceType)) {
+                // 如果是字符串,直接使用
+                $sourceTypeValue = $sourceType;
+            } else {
+                // 其他类型转换为字符串
+                $sourceTypeValue = (string)$sourceType;
+            }
+        }
+
         return ItemTransactionLog::create([
                                               'user_id'          => $userId,
                                               'item_id'          => $itemId,
                                               'instance_id'      => $instanceId,
                                               'quantity'         => $quantity,
                                               'transaction_type' => $transactionType,
-                                              'source_type'      => $sourceType,
+                                              'source_type'      => $sourceTypeValue,
                                               'source_id'        => $sourceId,
                                               'details'          => $details,
                                               'expire_at'        => $expireAt,

+ 7 - 0
app/Module/Mex/AdminControllers/MexOrderController.php

@@ -90,6 +90,9 @@ class MexOrderController extends AdminController
             ]);
             $helper->columnQuantity('completed_quantity', '已成交数量');
             $helper->columnAmount('completed_amount', '已成交金额');
+            $grid->column('last_match_failure_reason', '最后无法成交原因')->display(function ($value) {
+                return $value ? \Illuminate\Support\Str::limit($value, 50) : '-';
+            })->help('显示撮合过程中无法成交的具体原因');
             $helper->columnDatetime('created_at', '创建时间');
             $helper->columnDatetime('completed_at', '完成时间');
 
@@ -144,6 +147,9 @@ class MexOrderController extends AdminController
             $show->field('completed_quantity', '已成交数量');
             $show->field('completed_amount', '已成交金额');
             $show->field('failed_reason', '失败原因');
+            $show->field('last_match_failure_reason', '最后无法成交原因')->as(function ($value) {
+                return $value ?: '无';
+            });
             $show->field('created_at', '创建时间');
             $show->field('updated_at', '更新时间');
             $show->field('completed_at', '完成时间');
@@ -183,6 +189,7 @@ class MexOrderController extends AdminController
             $helper->display('completed_quantity', '已成交数量');
             $helper->display('completed_amount', '已成交金额');
             $helper->display('failed_reason', '失败原因');
+            $helper->display('last_match_failure_reason', '最后无法成交原因');
             $helper->display('created_at', '创建时间');
             $helper->display('updated_at', '更新时间');
             $helper->display('completed_at', '完成时间');

+ 50 - 0
app/Module/Mex/Logic/MexMatchLogic.php

@@ -246,6 +246,25 @@ class MexMatchLogic
                     ->limit($batchSize)
                     ->get();
 
+                // 为价格不符合条件的买入订单记录无法成交原因
+                MexOrder::where('item_id', $itemId)
+                    ->where('order_type', OrderType::BUY)
+                    ->where('status', OrderStatus::PENDING)
+                    ->where('price', '<', $priceConfig->max_price) // 价格低于最高价
+                    ->update([
+                        'last_match_failure_reason' => "价格验证失败:买入价格低于最高价格 {$priceConfig->max_price}"
+                    ]);
+
+                // 为数量超过保护阈值的买入订单记录无法成交原因
+                MexOrder::where('item_id', $itemId)
+                    ->where('order_type', OrderType::BUY)
+                    ->where('status', OrderStatus::PENDING)
+                    ->where('price', '>=', $priceConfig->max_price) // 价格符合条件
+                    ->where('quantity', '>', $priceConfig->protection_threshold) // 数量超过保护阈值
+                    ->update([
+                        'last_match_failure_reason' => "数量保护:订单数量超过保护阈值 {$priceConfig->protection_threshold}"
+                    ]);
+
                 if ($buyOrders->isEmpty()) {
                     $result = [
                         'success' => true,
@@ -269,6 +288,11 @@ class MexMatchLogic
                 foreach ($buyOrders as $order) {
                     // 检查库存是否充足(整单匹配原则)
                     if ($currentStock < $order->quantity) {
+                        // 记录库存不足的无法成交原因
+                        $order->update([
+                            'last_match_failure_reason' => "库存不足:当前库存 {$currentStock},需要 {$order->quantity}"
+                        ]);
+
                         // 库存不足时结束本次撮合处理,避免无效循环
                         break;
                     }
@@ -282,6 +306,16 @@ class MexMatchLogic
 
                         // 更新仓库对象的库存(用于后续订单判断)
                         $warehouse->quantity = $currentStock;
+
+                        // 清除之前的无法成交原因(如果有的话)
+                        if ($order->last_match_failure_reason) {
+                            $order->update(['last_match_failure_reason' => null]);
+                        }
+                    } else {
+                        // 记录撮合失败的原因
+                        $order->update([
+                            'last_match_failure_reason' => $matchResult['message']
+                        ]);
                     }
                 }
 
@@ -383,6 +417,12 @@ class MexMatchLogic
                             'min_price' => $priceConfig->min_price,
                             'price_compare' => bccomp($order->price, $priceConfig->min_price, 5)
                         ]);
+
+                        // 记录价格验证失败的无法成交原因
+                        $order->update([
+                            'last_match_failure_reason' => "价格验证失败:卖出价格 {$order->price} 高于最低价格 {$priceConfig->min_price}"
+                        ]);
+
                         continue; // 价格不符合条件,跳过此订单
                     }
 
@@ -395,11 +435,21 @@ class MexMatchLogic
                             'order_id' => $order->id,
                             'total_amount' => $matchResult['total_amount']
                         ]);
+
+                        // 清除之前的无法成交原因(如果有的话)
+                        if ($order->last_match_failure_reason) {
+                            $order->update(['last_match_failure_reason' => null]);
+                        }
                     } else {
                         Log::error('卖出订单撮合失败', [
                             'order_id' => $order->id,
                             'error_message' => $matchResult['message']
                         ]);
+
+                        // 记录撮合失败的原因
+                        $order->update([
+                            'last_match_failure_reason' => $matchResult['message']
+                        ]);
                     }
                 }
 

+ 3 - 1
app/Module/Mex/Models/MexOrder.php

@@ -29,13 +29,14 @@ use UCore\ModelCore;
  * @property  \Carbon\Carbon  $updated_at  更新时间
  * @property  \Carbon\Carbon  $completed_at  完成时间
  * @property  string  $failed_reason  失败原因
+ * @property  string  $last_match_failure_reason  最后无法成交原因,记录撮合过程中无法成交的具体原因
  * field end
  */
 class MexOrder extends ModelCore
 {
     protected $table = 'mex_orders';
 
-    // attrlist start 
+    // attrlist start
     protected $fillable = [
         'id',
         'user_id',
@@ -51,6 +52,7 @@ class MexOrder extends ModelCore
         'completed_amount',
         'completed_at',
         'failed_reason',
+        'last_match_failure_reason',
     ];
     // attrlist end