21日1840-修复农贸市场撮合事务问题解决挂单117无法成交.md 6.7 KB

修复农贸市场撮合事务问题,解决挂单117无法成交

任务时间: 2025年06月21日 18:40
任务类型: 问题修复
模块: Mex/撮合系统

任务概述

分析并修复挂单117无法成交的问题。通过深入调试发现根本原因是撮合逻辑中缺少事务处理,导致物品转移操作失败。最终通过在Service层为单个订单撮合添加事务处理成功解决问题。

问题分析

1. 问题现象

  • 挂单117:用户39077卖出100个辣椒,价格0.00900,总金额0.90000
  • 状态:一直处于PENDING状态,无法完成撮合
  • 撮合命令:运行撮合命令显示"撮合订单: 0 个"

2. 调试过程

第一步:检查基础数据

  • 挂单信息正常:价格、数量、状态都符合预期
  • 价格配置正常:0.00900 ≤ 0.01000(最低价格),符合撮合条件
  • 仓库有库存:625个辣椒

第二步:添加调试日志

在撮合逻辑中添加详细的调试日志,发现错误信息:

卖出订单撮合失败 {"order_id":117,"error_message":"用户卖出物品订单撮合失败:物品流转失败:物品转移异常:transaction level is 0"}

第三步:分析事务问题

发现撮合逻辑中的物品转移操作需要在事务中执行,但当前没有开启事务:

  • Logic层不应该开启事务(根据代码注释)
  • Service层应该处理事务,但当前Service层直接调用Logic层
  • 导致 Helper::check_tr() 检查失败,抛出"transaction level is 0"错误

3. 根本原因

事务架构问题

  • 撮合命令调用 MexMatchService::executeUserSellItemMatch()
  • 该方法直接调用 MexMatchLogic::executeUserSellItemMatch()
  • Logic层中的单个订单撮合方法 executeUserSellItemOrderMatch() 需要事务
  • 但整个调用链中没有开启事务

解决方案

1. 架构设计原则

根据你的要求:"批处理就启动事务,不合理,应该单个处理再启动事务"

2. 技术实现

在Service层添加带事务的单个订单撮合方法

/**
 * 执行单个用户卖出物品订单的撮合(带事务处理)
 */
public static function executeUserSellItemOrderMatchWithTransaction(\App\Module\Mex\Models\MexOrder $order): array
{
    try {
        return DB::transaction(function () use ($order) {
            return \App\Module\Mex\Logic\MexMatchLogic::executeUserSellItemOrderMatch($order);
        });
    } catch (\Exception $e) {
        return [
            'success' => false,
            'message' => '用户卖出物品订单撮合失败:' . $e->getMessage(),
            'order_id' => $order->id,
            'total_amount' => '0.00000',
        ];
    }
}

修改Logic层调用Service层的带事务方法

// 修改前
$matchResult = self::executeUserSellItemOrderMatch($order);

// 修改后  
$matchResult = \App\Module\Mex\Services\MexMatchService::executeUserSellItemOrderMatchWithTransaction($order);

将Logic层方法改为public

// 修改前
private static function executeUserSellItemOrderMatch(MexOrder $order): array

// 修改后
public static function executeUserSellItemOrderMatch(MexOrder $order): array

3. 修改文件

  1. app/Module/Mex/Services/MexMatchService.php
  2. app/Module/Mex/Logic/MexMatchLogic.php

测试验证

1. 解决资金不足问题

在测试过程中发现仓库账户资金不足,使用FundService给仓库账户充值:

use App\Module\Fund\Services\FundService;
use App\Module\Fund\Enums\FUND_TYPE;
$fundService = new FundService(15, 2);
$result = $fundService->admin_operate(1, FUND_TYPE::FUND2, 100000000000.0, '仓库账户充值');

2. 撮合测试结果

php artisan mex:user-sell-item-match --item=3

测试结果

  • 撮合订单: 1 个 ✅
  • 成交金额: 0.90000 ✅
  • 挂单117状态: COMPLETED ✅
  • 成交记录: 已创建 ✅

3. 数据验证

挂单状态

SELECT * FROM kku_mex_orders WHERE id = 117;
  • status: "COMPLETED"
  • completed_quantity: 100
  • completed_amount: "0.90000"
  • completed_at: "2025-06-21T18:39:54.000Z"

成交记录

SELECT * FROM kku_mex_transactions WHERE sell_order_id = 117;
  • transaction_type: "USER_SELL"
  • quantity: 100
  • price: "0.00900"
  • total_amount: "0.90000"
  • buyer_id: 15 (仓库账户)
  • seller_id: 39077

技术细节

1. 事务处理策略

  • 批处理级别:不开启事务,允许部分成功
  • 单个订单级别:开启事务,保证原子性
  • 好处:避免长事务锁定,提高并发性能

2. 错误处理改进

  • 添加详细的调试日志
  • 区分价格验证失败和撮合执行失败
  • 记录具体的错误信息便于排查

3. 架构一致性

  • Service层负责事务管理
  • Logic层专注业务逻辑
  • 保持代码架构的清晰分层

影响范围

  • 修改文件: 2个(Service和Logic)
  • 影响功能: 农贸市场撮合系统
  • 风险评估: 低风险,只是添加事务保护
  • 性能影响: 轻微,单个订单事务开销很小

经验总结

1. 问题排查方法

  1. 检查基础数据和配置
  2. 添加详细的调试日志
  3. 分析错误信息找到根本原因
  4. 验证架构设计是否合理

2. 事务设计原则

  • 事务范围应该尽可能小
  • 批处理不应该开启大事务
  • 单个业务操作需要事务保护

3. 代码架构

  • Service层处理事务和异常
  • Logic层专注业务逻辑
  • 保持层次分明,职责清晰

提交信息

修复农贸市场撮合事务问题,解决挂单117无法成交的问题

问题分析:
- 挂单117无法成交的根本原因是撮合逻辑中缺少事务处理
- 物品转移操作需要在事务中执行,但Logic层没有开启事务
- 导致物品转移时出现'transaction level is 0'错误

解决方案:
- 在Service层为单个订单撮合添加事务处理
- 创建executeUserSellItemOrderMatchWithTransaction和executeUserBuyItemOrderMatchWithTransaction方法
- 修改Logic层调用Service层的带事务方法
- 将executeUserSellItemOrderMatch和executeUserBuyItemOrderMatch方法改为public

技术实现:
- 使用DB::transaction()为单个订单撮合提供事务保护
- 保持批处理不开启事务,只在单个订单处理时开启事务
- 添加详细的错误日志记录,便于问题排查

测试验证:
- 给仓库账户充值足够资金解决资金不足问题
- 挂单117成功撮合,状态变为COMPLETED
- 创建了对应的成交记录,撮合功能正常工作

后续建议

  1. 监控撮合性能:关注事务添加后的性能影响
  2. 完善错误处理:继续优化错误信息的准确性
  3. 自动化测试:为撮合功能添加自动化测试用例
  4. 仓库资金管理:建立仓库账户资金监控机制