Bladeren bron

feat(GameItems): 重构Item服务逻辑并扩展功能

- 重构ItemService服务类,优化代码结构
- 扩展Item逻辑类,新增452行功能代码
- 更新GameItems模块文档
- 新增ItemQuantityChanged事件类
- 调整GameItemsServiceProvider注册逻辑
Your Name 8 maanden geleden
bovenliggende
commit
3723f63711

+ 32 - 9
app/Module/GameItems/Docs/README.md

@@ -2637,14 +2637,15 @@ public function sendItemByMail($fromUserId, $toUserId, $itemId, $quantity, $mess
 
 系统定义了以下物品相关事件:
 
-1. **ItemAdded**:物品添加到用户背包时触发
-2. **ItemRemoved**:物品从用户背包移除时触发
-3. **ItemUsed**:物品被使用时触发
-4. **ItemTraded**:物品被交易时触发
-5. **ChestOpened**:宝箱被开启时触发
-6. **ItemCrafted**:物品被合成时触发
-7. **ItemDismantled**:物品被分解时触发
-8. **ItemBound**:物品被绑定时触发
+1. **ItemAcquired**:物品添加到用户背包时触发
+2. **ItemConsumed**:物品从用户背包消耗时触发
+3. **ItemQuantityChanged**:物品数量变更时触发(增加或减少)
+4. **ItemUsed**:物品被使用时触发
+5. **ItemTraded**:物品被交易时触发
+6. **ChestOpened**:宝箱被开启时触发
+7. **ItemCrafted**:物品被合成时触发
+8. **ItemDismantled**:物品被分解时触发
+9. **ItemBound**:物品被绑定时触发
 
 #### 12.2.2 事件监听
 
@@ -2655,7 +2656,8 @@ public function sendItemByMail($fromUserId, $toUserId, $itemId, $quantity, $mess
 // 在服务提供者中注册事件监听
 public function boot()
 {
-    Event::listen(ItemAdded::class, function ($event) {
+    // 监听物品获取事件
+    Event::listen(ItemAcquired::class, function ($event) {
         // 更新任务进度
         $this->questService->updateItemCollectionProgress(
             $event->userId,
@@ -2670,6 +2672,27 @@ public function boot()
             $event->quantity
         );
     });
+
+    // 监听物品数量变更事件
+    Event::listen(ItemQuantityChanged::class, function ($event) {
+        // 记录物品数量变更
+        \Log::info('物品数量变更', [
+            'user_id' => $event->userId,
+            'item_id' => $event->itemId,
+            'old_quantity' => $event->oldQuantity,
+            'new_quantity' => $event->newQuantity,
+            'change_amount' => $event->changeAmount,
+        ]);
+
+        // 触发相关系统更新
+        if ($event->changeAmount > 0) {
+            // 物品数量增加的处理
+            $this->inventoryService->checkInventoryAchievements($event->userId, $event->itemId, $event->newQuantity);
+        } else {
+            // 物品数量减少的处理
+            $this->inventoryService->checkInventoryQuests($event->userId, $event->itemId, $event->newQuantity);
+        }
+    });
 }
 ```
 

+ 99 - 0
app/Module/GameItems/Events/ItemQuantityChanged.php

@@ -0,0 +1,99 @@
+<?php
+
+namespace App\Module\GameItems\Events;
+
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+
+class ItemQuantityChanged
+{
+    use Dispatchable, InteractsWithSockets, SerializesModels;
+
+    /**
+     * 用户ID
+     *
+     * @var int
+     */
+    public $userId;
+
+    /**
+     * 物品ID
+     *
+     * @var int
+     */
+    public $itemId;
+
+    /**
+     * 物品实例ID(单独属性物品)
+     *
+     * @var int|null
+     */
+    public $instanceId;
+
+    /**
+     * 旧数量
+     *
+     * @var int
+     */
+    public $oldQuantity;
+
+    /**
+     * 新数量
+     *
+     * @var int
+     */
+    public $newQuantity;
+
+    /**
+     * 变化量(正数表示增加,负数表示减少)
+     *
+     * @var int
+     */
+    public $changeAmount;
+
+    /**
+     * 用户物品记录ID
+     *
+     * @var int
+     */
+    public $userItemId;
+
+    /**
+     * 选项
+     *
+     * @var array
+     */
+    public $options;
+
+    /**
+     * 创建一个新的事件实例
+     *
+     * @param int $userId 用户ID
+     * @param int $itemId 物品ID
+     * @param int|null $instanceId 物品实例ID
+     * @param int $oldQuantity 旧数量
+     * @param int $newQuantity 新数量
+     * @param int $userItemId 用户物品记录ID
+     * @param array $options 选项
+     * @return void
+     */
+    public function __construct(
+        int $userId,
+        int $itemId,
+        ?int $instanceId,
+        int $oldQuantity,
+        int $newQuantity,
+        int $userItemId,
+        array $options = []
+    ) {
+        $this->userId = $userId;
+        $this->itemId = $itemId;
+        $this->instanceId = $instanceId;
+        $this->oldQuantity = $oldQuantity;
+        $this->newQuantity = $newQuantity;
+        $this->changeAmount = $newQuantity - $oldQuantity;
+        $this->userItemId = $userItemId;
+        $this->options = $options;
+    }
+}

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

@@ -2,7 +2,18 @@
 
 namespace App\Module\GameItems\Logics;
 
+use App\Module\GameItems\Enums\ITEM_TYPE;
+use App\Module\GameItems\Enums\TRANSACTION_TYPE;
+use App\Module\GameItems\Events\ItemAcquired;
+use App\Module\GameItems\Events\ItemConsumed;
+use App\Module\GameItems\Events\ItemQuantityChanged;
 use App\Module\GameItems\Models\Item as ItemModel;
+use App\Module\GameItems\Models\ItemInstance;
+use App\Module\GameItems\Models\ItemTransactionLog;
+use App\Module\GameItems\Models\ItemUser;
+use Exception;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Event;
 
 /**
  * 物品逻辑类
@@ -15,9 +26,9 @@ class Item
      * @param ItemModel $item 物品模型
      * @return bool
      */
-    public function isChest(ItemModel $item): bool
+    public static function isChest(ItemModel $item): bool
     {
-        return $item->type == 5; // 5表示宝箱类型
+        return $item->type == ITEM_TYPE::CHEST; // 使用枚举代替魔法数字
     }
 
     /**
@@ -26,7 +37,7 @@ class Item
      * @param ItemModel $item 物品模型
      * @return bool
      */
-    public function isExpired(ItemModel $item): bool
+    public static function isExpired(ItemModel $item): bool
     {
         if (empty($item->global_expire_at)) {
             return false;
@@ -34,4 +45,439 @@ class Item
 
         return $item->global_expire_at->isPast();
     }
+
+    /**
+     * 添加统一属性物品
+     *
+     * @param int $userId 用户ID
+     * @param int $itemId 物品ID
+     * @param int $quantity 数量
+     * @param array $options 选项
+     * @return array 添加结果
+     * @throws Exception
+     */
+    public static function addNormalItem(int $userId, int $itemId, int $quantity, array $options = []): array
+    {
+        // 获取物品信息
+        $item = ItemModel::findOrFail($itemId);
+
+        // 计算过期时间
+        $expireAt = null;
+        if (!empty($options['expire_at'])) {
+            $expireAt = $options['expire_at'];
+        } elseif ($item->default_expire_seconds > 0) {
+            $expireAt = now()->addSeconds($item->default_expire_seconds);
+        }
+
+        // 获取来源信息
+        $sourceType = $options['source_type'] ?? null;
+        $sourceId = $options['source_id'] ?? null;
+
+        // 开始事务
+        DB::beginTransaction();
+        try {
+            // 检查用户是否已有该物品且过期时间相同
+            $userItem = ItemUser::where('user_id', $userId)
+                ->where('item_id', $itemId)
+                ->where(function ($query) use ($expireAt) {
+                    if ($expireAt === null) {
+                        $query->whereNull('expire_at');
+                    } else {
+                        $query->where('expire_at', $expireAt);
+                    }
+                })
+                ->whereNull('instance_id')
+                ->first();
+
+            $addedQuantity = $quantity;
+            $currentQuantity = 0;
+
+            if ($userItem) {
+                // 已有物品,增加数量
+                $currentQuantity = $userItem->quantity;
+                $newQuantity = $currentQuantity + $quantity;
+
+                // 检查最大堆叠限制
+                if ($item->max_stack > 0 && $newQuantity > $item->max_stack) {
+                    // 超过最大堆叠,创建新堆叠
+                    $userItem->quantity = $item->max_stack;
+                    $userItem->save();
+
+                    // 剩余数量
+                    $remainingQuantity = $newQuantity - $item->max_stack;
+
+                    // 递归添加剩余数量
+                    self::addNormalItem($userId, $itemId, $remainingQuantity, $options);
+
+                    $addedQuantity = $quantity - $remainingQuantity;
+                    $currentQuantity = $item->max_stack;
+                } else {
+                    // 未超过最大堆叠,直接更新数量
+                    $oldQuantity = $userItem->quantity;
+                    $userItem->quantity = $newQuantity;
+                    $userItem->save();
+                    $currentQuantity = $newQuantity;
+
+                    // 触发物品数量变更事件
+                    Event::dispatch(new ItemQuantityChanged(
+                        $userId,
+                        $itemId,
+                        null,
+                        $oldQuantity,
+                        $newQuantity,
+                        $userItem->id,
+                        $options
+                    ));
+                }
+            } else {
+                // 没有该物品,创建新记录
+                $userItem = new ItemUser([
+                    'user_id' => $userId,
+                    'item_id' => $itemId,
+                    'quantity' => min($quantity, $item->max_stack > 0 ? $item->max_stack : $quantity),
+                    'expire_at' => $expireAt,
+                ]);
+                $userItem->save();
+
+                // 如果数量超过最大堆叠,递归添加剩余数量
+                if ($item->max_stack > 0 && $quantity > $item->max_stack) {
+                    $remainingQuantity = $quantity - $item->max_stack;
+                    self::addNormalItem($userId, $itemId, $remainingQuantity, $options);
+                    $addedQuantity = $item->max_stack;
+                }
+
+                $currentQuantity = $userItem->quantity;
+            }
+
+            // 记录交易日志
+            self::logTransaction(
+                $userId,
+                $itemId,
+                null,
+                $addedQuantity,
+                TRANSACTION_TYPE::ACQUIRE,
+                $sourceType,
+                $sourceId,
+                $options['details'] ?? null,
+                $expireAt,
+                $options['ip_address'] ?? null,
+                $options['device_info'] ?? null
+            );
+
+            // 触发物品获取事件
+            Event::dispatch(new ItemAcquired($userId, $itemId, null, $addedQuantity, $options));
+
+            DB::commit();
+
+            return [
+                'success' => true,
+                'item_id' => $itemId,
+                'quantity' => $addedQuantity,
+                'current_quantity' => $currentQuantity,
+                'user_item_id' => $userItem->id,
+            ];
+        } catch (Exception $e) {
+            DB::rollBack();
+            throw $e;
+        }
+    }
+
+    /**
+     * 添加单独属性物品
+     *
+     * @param int $userId 用户ID
+     * @param int $itemId 物品ID
+     * @param array $options 选项
+     * @return array 添加结果
+     * @throws Exception
+     */
+    public static function addUniqueItem(int $userId, int $itemId, array $options = []): array
+    {
+        // 获取物品信息
+        $item = ItemModel::findOrFail($itemId);
+
+        // 确保物品是单独属性物品
+        if (!$item->is_unique) {
+            throw new Exception("物品 {$itemId} 不是单独属性物品");
+        }
+
+        // 计算过期时间
+        $expireAt = null;
+        if (!empty($options['expire_at'])) {
+            $expireAt = $options['expire_at'];
+        } elseif ($item->default_expire_seconds > 0) {
+            $expireAt = now()->addSeconds($item->default_expire_seconds);
+        }
+
+        // 获取来源信息
+        $sourceType = $options['source_type'] ?? null;
+        $sourceId = $options['source_id'] ?? null;
+
+        // 开始事务
+        DB::beginTransaction();
+        try {
+            // 创建物品实例
+            $instance = new ItemInstance([
+                'item_id' => $itemId,
+                'name' => $options['name'] ?? $item->name,
+                'display_attributes' => $options['display_attributes'] ?? $item->display_attributes,
+                'numeric_attributes' => $options['numeric_attributes'] ?? $item->numeric_attributes,
+                'tradable' => $options['tradable'] ?? $item->tradable,
+                'is_bound' => $options['is_bound'] ?? false,
+                'bound_to' => $options['bound_to'] ?? null,
+                'bind_exp_time' => $options['bind_exp_time'] ?? null,
+                'expire_at' => $expireAt,
+            ]);
+            $instance->save();
+
+            // 关联到用户
+            $userItem = new ItemUser([
+                'user_id' => $userId,
+                'item_id' => $itemId,
+                'instance_id' => $instance->id,
+                'quantity' => 1, // 单独属性物品数量始终为1
+                'expire_at' => $expireAt,
+            ]);
+            $userItem->save();
+
+            // 记录交易日志
+            self::logTransaction(
+                $userId,
+                $itemId,
+                $instance->id,
+                1,
+                TRANSACTION_TYPE::ACQUIRE,
+                $sourceType,
+                $sourceId,
+                $options['details'] ?? null,
+                $expireAt,
+                $options['ip_address'] ?? null,
+                $options['device_info'] ?? null
+            );
+
+            // 触发物品获取事件
+            Event::dispatch(new ItemAcquired($userId, $itemId, $instance->id, 1, $options));
+
+            DB::commit();
+
+            return [
+                'success' => true,
+                'item_id' => $itemId,
+                'instance_id' => $instance->id,
+                'user_item_id' => $userItem->id,
+            ];
+        } catch (Exception $e) {
+            DB::rollBack();
+            throw $e;
+        }
+    }
+
+    /**
+     * 消耗统一属性物品
+     *
+     * @param int $userId 用户ID
+     * @param int $itemId 物品ID
+     * @param int $quantity 数量
+     * @param array $options 选项
+     * @return array 消耗结果
+     * @throws Exception
+     */
+    public static function consumeNormalItem(int $userId, int $itemId, int $quantity, array $options = []): array
+    {
+        // 获取用户物品
+        $userItems = ItemUser::where('user_id', $userId)
+            ->where('item_id', $itemId)
+            ->whereNull('instance_id')
+            ->orderBy('expire_at') // 优先消耗即将过期的物品
+            ->get();
+
+        // 检查物品数量是否足够
+        $totalQuantity = $userItems->sum('quantity');
+        if ($totalQuantity < $quantity) {
+            throw new Exception("用户 {$userId} 的物品 {$itemId} 数量不足,需要 {$quantity},实际 {$totalQuantity}");
+        }
+
+        // 获取来源信息
+        $sourceType = $options['source_type'] ?? null;
+        $sourceId = $options['source_id'] ?? null;
+
+        // 开始消耗物品
+        $remainingQuantity = $quantity;
+        foreach ($userItems as $userItem) {
+            if ($remainingQuantity <= 0) {
+                break;
+            }
+
+            if ($userItem->quantity <= $remainingQuantity) {
+                // 当前堆叠数量不足,全部消耗
+                $consumedQuantity = $userItem->quantity;
+                $remainingQuantity -= $consumedQuantity;
+
+                // 记录交易日志
+                self::logTransaction(
+                    $userId,
+                    $itemId,
+                    null,
+                    -$consumedQuantity,
+                    TRANSACTION_TYPE::CONSUME,
+                    $sourceType,
+                    $sourceId,
+                    $options['details'] ?? null,
+                    null,
+                    $options['ip_address'] ?? null,
+                    $options['device_info'] ?? null
+                );
+
+                // 删除用户物品记录
+                $userItem->delete();
+            } else {
+                // 当前堆叠数量足够,部分消耗
+                $consumedQuantity = $remainingQuantity;
+                $oldQuantity = $userItem->quantity;
+                $newQuantity = $oldQuantity - $consumedQuantity;
+                $userItem->quantity = $newQuantity;
+                $userItem->save();
+                $remainingQuantity = 0;
+
+                // 记录交易日志
+                self::logTransaction(
+                    $userId,
+                    $itemId,
+                    null,
+                    -$consumedQuantity,
+                    TRANSACTION_TYPE::CONSUME,
+                    $sourceType,
+                    $sourceId,
+                    $options['details'] ?? null,
+                    null,
+                    $options['ip_address'] ?? null,
+                    $options['device_info'] ?? null
+                );
+
+                // 触发物品数量变更事件
+                Event::dispatch(new ItemQuantityChanged(
+                    $userId,
+                    $itemId,
+                    null,
+                    $oldQuantity,
+                    $newQuantity,
+                    $userItem->id,
+                    $options
+                ));
+            }
+
+            // 触发物品消耗事件
+            Event::dispatch(new ItemConsumed($userId, $itemId, null, $consumedQuantity, $options));
+        }
+
+        return [
+            'success' => true,
+            'item_id' => $itemId,
+            'quantity' => $quantity,
+            'remaining_quantity' => $totalQuantity - $quantity,
+        ];
+    }
+
+    /**
+     * 消耗单独属性物品
+     *
+     * @param int $userId 用户ID
+     * @param int $itemId 物品ID
+     * @param int $instanceId 物品实例ID
+     * @param array $options 选项
+     * @return array 消耗结果
+     * @throws Exception
+     */
+    public static function consumeUniqueItem(int $userId, int $itemId, int $instanceId, array $options = []): array
+    {
+        // 获取用户物品
+        $userItem = ItemUser::where('user_id', $userId)
+            ->where('item_id', $itemId)
+            ->where('instance_id', $instanceId)
+            ->first();
+
+        if (!$userItem) {
+            throw new Exception("用户 {$userId} 没有物品实例 {$instanceId}");
+        }
+
+        // 获取来源信息
+        $sourceType = $options['source_type'] ?? null;
+        $sourceId = $options['source_id'] ?? null;
+
+        // 记录交易日志
+        self::logTransaction(
+            $userId,
+            $itemId,
+            $instanceId,
+            -1,
+            TRANSACTION_TYPE::CONSUME,
+            $sourceType,
+            $sourceId,
+            $options['details'] ?? null,
+            null,
+            $options['ip_address'] ?? null,
+            $options['device_info'] ?? null
+        );
+
+        // 删除用户物品记录
+        $userItem->delete();
+
+        // 是否删除物品实例
+        if (!empty($options['delete_instance'])) {
+            ItemInstance::where('id', $instanceId)->delete();
+        }
+
+        // 触发物品消耗事件
+        Event::dispatch(new ItemConsumed($userId, $itemId, $instanceId, 1, $options));
+
+        return [
+            'success' => true,
+            'item_id' => $itemId,
+            'instance_id' => $instanceId,
+        ];
+    }
+
+    /**
+     * 记录物品交易日志
+     *
+     * @param int $userId 用户ID
+     * @param int $itemId 物品ID
+     * @param int|null $instanceId 物品实例ID
+     * @param int $quantity 数量
+     * @param int $transactionType 交易类型
+     * @param string|null $sourceType 来源类型
+     * @param int|null $sourceId 来源ID
+     * @param array|null $details 详细信息
+     * @param string|null $expireAt 过期时间
+     * @param string|null $ipAddress IP地址
+     * @param string|null $deviceInfo 设备信息
+     * @return ItemTransactionLog
+     */
+    public static function logTransaction(
+        int $userId,
+        int $itemId,
+        ?int $instanceId,
+        int $quantity,
+        int $transactionType,
+        ?string $sourceType = null,
+        ?int $sourceId = null,
+        ?array $details = null,
+        ?string $expireAt = null,
+        ?string $ipAddress = null,
+        ?string $deviceInfo = null
+    ): ItemTransactionLog {
+        return ItemTransactionLog::create([
+            'user_id' => $userId,
+            'item_id' => $itemId,
+            'instance_id' => $instanceId,
+            'quantity' => $quantity,
+            'transaction_type' => $transactionType,
+            'source_type' => $sourceType,
+            'source_id' => $sourceId,
+            'details' => $details,
+            'expire_at' => $expireAt,
+            'ip_address' => $ipAddress,
+            'device_info' => $deviceInfo,
+        ]);
+    }
 }

+ 25 - 0
app/Module/GameItems/Providers/GameItemsServiceProvider.php

@@ -2,6 +2,8 @@
 
 namespace App\Module\GameItems\Providers;
 
+use App\Module\GameItems\Events\ItemQuantityChanged;
+use Illuminate\Support\Facades\Event;
 use Illuminate\Support\ServiceProvider;
 
 class GameItemsServiceProvider extends ServiceProvider
@@ -30,5 +32,28 @@ class GameItemsServiceProvider extends ServiceProvider
                 \App\Module\GameItems\Commands\GenerateChestJsonCommand::class,
             ]);
         }
+
+        // 注册事件监听器
+        $this->registerEventListeners();
+    }
+
+    /**
+     * 注册事件监听器
+     *
+     * @return void
+     */
+    protected function registerEventListeners()
+    {
+        // 物品数量变更事件监听器示例
+        // Event::listen(ItemQuantityChanged::class, function (ItemQuantityChanged $event) {
+        //     // 在这里处理物品数量变更事件
+        //     \Log::info('物品数量变更', [
+        //         'user_id' => $event->userId,
+        //         'item_id' => $event->itemId,
+        //         'old_quantity' => $event->oldQuantity,
+        //         'new_quantity' => $event->newQuantity,
+        //         'change_amount' => $event->changeAmount,
+        //     ]);
+        // });
     }
 }

+ 30 - 432
app/Module/GameItems/Services/ItemService.php

@@ -2,20 +2,28 @@
 
 namespace App\Module\GameItems\Services;
 
-use App\Module\GameItems\Enums\TRANSACTION_TYPE;
-use App\Module\GameItems\Events\ItemAcquired;
-use App\Module\GameItems\Events\ItemConsumed;
-use App\Module\GameItems\Models\ItemInstance;
+use App\Module\GameItems\Logics\Item as ItemLogic;
 use App\Module\GameItems\Models\ItemItem;
-use App\Module\GameItems\Models\ItemTransactionLog;
 use App\Module\GameItems\Models\ItemUser;
 use Exception;
 use Illuminate\Database\Eloquent\Collection;
-use Illuminate\Support\Facades\DB;
-use Illuminate\Support\Facades\Event;
 
 class ItemService
 {
+    /**
+     * 物品逻辑类
+     *
+     * @var ItemLogic
+     */
+    private $itemLogic;
+
+    /**
+     * 构造函数
+     */
+    public function __construct()
+    {
+        $this->itemLogic = new ItemLogic();
+    }
     /**
      * 获取用户物品列表
      *
@@ -78,227 +86,18 @@ class ItemService
         // 获取物品信息
         $item = ItemItem::findOrFail($itemId);
 
+        // 检查物品是否已过期(全局过期)
+        if ($this->itemLogic->isExpired($item)) {
+            throw new Exception("物品 {$itemId} 已全局过期");
+        }
+
         // 处理单独属性物品
         if ($item->is_unique) {
-            return $this->addUniqueItem($userId, $itemId, $options);
+            return $this->itemLogic->addUniqueItem($userId, $itemId, $options);
         }
 
         // 处理统一属性物品
-        return $this->addNormalItem($userId, $itemId, $quantity, $options);
-    }
-
-    /**
-     * 添加统一属性物品
-     *
-     * @param int $userId 用户ID
-     * @param int $itemId 物品ID
-     * @param int $quantity 数量
-     * @param array $options 选项
-     * @return array 添加结果
-     * @throws Exception
-     */
-    protected function addNormalItem(int $userId, int $itemId, int $quantity, array $options = []): array
-    {
-        // 获取物品信息
-        $item = ItemItem::findOrFail($itemId);
-
-        // 计算过期时间
-        $expireAt = null;
-        if (!empty($options['expire_at'])) {
-            $expireAt = $options['expire_at'];
-        } elseif ($item->default_expire_seconds > 0) {
-            $expireAt = now()->addSeconds($item->default_expire_seconds);
-        }
-
-        // 获取来源信息
-        $sourceType = $options['source_type'] ?? null;
-        $sourceId = $options['source_id'] ?? null;
-
-        // 开始事务
-        DB::beginTransaction();
-        try {
-            // 检查用户是否已有该物品且过期时间相同
-            $userItem = ItemUser::where('user_id', $userId)
-                ->where('item_id', $itemId)
-                ->where(function ($query) use ($expireAt) {
-                    if ($expireAt === null) {
-                        $query->whereNull('expire_at');
-                    } else {
-                        $query->where('expire_at', $expireAt);
-                    }
-                })
-                ->whereNull('instance_id')
-                ->first();
-
-            $addedQuantity = $quantity;
-            $currentQuantity = 0;
-
-            if ($userItem) {
-                // 已有物品,增加数量
-                $currentQuantity = $userItem->quantity;
-                $newQuantity = $currentQuantity + $quantity;
-
-                // 检查最大堆叠限制
-                if ($item->max_stack > 0 && $newQuantity > $item->max_stack) {
-                    // 超过最大堆叠,创建新堆叠
-                    $userItem->quantity = $item->max_stack;
-                    $userItem->save();
-
-                    // 剩余数量
-                    $remainingQuantity = $newQuantity - $item->max_stack;
-
-                    // 递归添加剩余数量
-                    $this->addNormalItem($userId, $itemId, $remainingQuantity, $options);
-
-                    $addedQuantity = $quantity - $remainingQuantity;
-                    $currentQuantity = $item->max_stack;
-                } else {
-                    // 未超过最大堆叠,直接更新数量
-                    $userItem->quantity = $newQuantity;
-                    $userItem->save();
-                    $currentQuantity = $newQuantity;
-                }
-            } else {
-                // 没有该物品,创建新记录
-                $userItem = new ItemUser([
-                    'user_id' => $userId,
-                    'item_id' => $itemId,
-                    'quantity' => min($quantity, $item->max_stack > 0 ? $item->max_stack : $quantity),
-                    'expire_at' => $expireAt,
-                ]);
-                $userItem->save();
-
-                // 如果数量超过最大堆叠,递归添加剩余数量
-                if ($item->max_stack > 0 && $quantity > $item->max_stack) {
-                    $remainingQuantity = $quantity - $item->max_stack;
-                    $this->addNormalItem($userId, $itemId, $remainingQuantity, $options);
-                    $addedQuantity = $item->max_stack;
-                }
-
-                $currentQuantity = $userItem->quantity;
-            }
-
-            // 记录交易日志
-            $this->logTransaction(
-                $userId,
-                $itemId,
-                null,
-                $addedQuantity,
-                TRANSACTION_TYPE::ACQUIRE,
-                $sourceType,
-                $sourceId,
-                $options['details'] ?? null,
-                $expireAt,
-                $options['ip_address'] ?? null,
-                $options['device_info'] ?? null
-            );
-
-            // 触发物品获取事件
-            Event::dispatch(new ItemAcquired($userId, $itemId, null, $addedQuantity, $options));
-
-            DB::commit();
-
-            return [
-                'success' => true,
-                'item_id' => $itemId,
-                'quantity' => $addedQuantity,
-                'current_quantity' => $currentQuantity,
-                'user_item_id' => $userItem->id,
-            ];
-        } catch (Exception $e) {
-            DB::rollBack();
-            throw $e;
-        }
-    }
-
-    /**
-     * 添加单独属性物品
-     *
-     * @param int $userId 用户ID
-     * @param int $itemId 物品ID
-     * @param array $options 选项
-     * @return array 添加结果
-     * @throws Exception
-     */
-    protected function addUniqueItem(int $userId, int $itemId, array $options = []): array
-    {
-        // 获取物品信息
-        $item = ItemItem::findOrFail($itemId);
-
-        // 确保物品是单独属性物品
-        if (!$item->is_unique) {
-            throw new Exception("物品 {$itemId} 不是单独属性物品");
-        }
-
-        // 计算过期时间
-        $expireAt = null;
-        if (!empty($options['expire_at'])) {
-            $expireAt = $options['expire_at'];
-        } elseif ($item->default_expire_seconds > 0) {
-            $expireAt = now()->addSeconds($item->default_expire_seconds);
-        }
-
-        // 获取来源信息
-        $sourceType = $options['source_type'] ?? null;
-        $sourceId = $options['source_id'] ?? null;
-
-        // 开始事务
-        DB::beginTransaction();
-        try {
-            // 创建物品实例
-            $instance = new ItemInstance([
-                'item_id' => $itemId,
-                'name' => $options['name'] ?? $item->name,
-                'display_attributes' => $options['display_attributes'] ?? $item->display_attributes,
-                'numeric_attributes' => $options['numeric_attributes'] ?? $item->numeric_attributes,
-                'tradable' => $options['tradable'] ?? $item->tradable,
-                'is_bound' => $options['is_bound'] ?? false,
-                'bound_to' => $options['bound_to'] ?? null,
-                'bind_exp_time' => $options['bind_exp_time'] ?? null,
-                'expire_at' => $expireAt,
-            ]);
-            $instance->save();
-
-            // 关联到用户
-            $userItem = new ItemUser([
-                'user_id' => $userId,
-                'item_id' => $itemId,
-                'instance_id' => $instance->id,
-                'quantity' => 1, // 单独属性物品数量始终为1
-                'expire_at' => $expireAt,
-            ]);
-            $userItem->save();
-
-            // 记录交易日志
-            $this->logTransaction(
-                $userId,
-                $itemId,
-                $instance->id,
-                1,
-                TRANSACTION_TYPE::ACQUIRE,
-                $sourceType,
-                $sourceId,
-                $options['details'] ?? null,
-                $expireAt,
-                $options['ip_address'] ?? null,
-                $options['device_info'] ?? null
-            );
-
-            // 触发物品获取事件
-            Event::dispatch(new ItemAcquired($userId, $itemId, $instance->id, 1, $options));
-
-            DB::commit();
-
-            return [
-                'success' => true,
-                'item_id' => $itemId,
-                'instance_id' => $instance->id,
-                'user_item_id' => $userItem->id,
-            ];
-        } catch (Exception $e) {
-            DB::rollBack();
-            throw $e;
-        }
+        return $this->itemLogic->addNormalItem($userId, $itemId, $quantity, $options);
     }
 
     /**
@@ -314,218 +113,17 @@ class ItemService
      */
     public function consumeItem(int $userId, int $itemId, ?int $instanceId, int $quantity, array $options = []): array
     {
-        // 开始事务
-        DB::beginTransaction();
-        try {
-            if ($instanceId) {
-                // 消耗单独属性物品
-                $result = $this->consumeUniqueItem($userId, $itemId, $instanceId, $options);
-            } else {
-                // 消耗统一属性物品
-                $result = $this->consumeNormalItem($userId, $itemId, $quantity, $options);
-            }
-
-            DB::commit();
-            return $result;
-        } catch (Exception $e) {
-            DB::rollBack();
-            throw $e;
-        }
-    }
-
-    /**
-     * 消耗统一属性物品
-     *
-     * @param int $userId 用户ID
-     * @param int $itemId 物品ID
-     * @param int $quantity 数量
-     * @param array $options 选项
-     * @return array 消耗结果
-     * @throws Exception
-     */
-    protected function consumeNormalItem(int $userId, int $itemId, int $quantity, array $options = []): array
-    {
-        // 获取用户物品
-        $userItems = ItemUser::where('user_id', $userId)
-            ->where('item_id', $itemId)
-            ->whereNull('instance_id')
-            ->orderBy('expire_at') // 优先消耗即将过期的物品
-            ->get();
-
-        // 检查物品数量是否足够
-        $totalQuantity = $userItems->sum('quantity');
-        if ($totalQuantity < $quantity) {
-            throw new Exception("用户 {$userId} 的物品 {$itemId} 数量不足,需要 {$quantity},实际 {$totalQuantity}");
-        }
-
-        // 获取来源信息
-        $sourceType = $options['source_type'] ?? null;
-        $sourceId = $options['source_id'] ?? null;
-
-        // 开始消耗物品
-        $remainingQuantity = $quantity;
-        foreach ($userItems as $userItem) {
-            if ($remainingQuantity <= 0) {
-                break;
-            }
-
-            if ($userItem->quantity <= $remainingQuantity) {
-                // 当前堆叠数量不足,全部消耗
-                $consumedQuantity = $userItem->quantity;
-                $remainingQuantity -= $consumedQuantity;
-
-                // 记录交易日志
-                $this->logTransaction(
-                    $userId,
-                    $itemId,
-                    null,
-                    -$consumedQuantity,
-                    TRANSACTION_TYPE::CONSUME,
-                    $sourceType,
-                    $sourceId,
-                    $options['details'] ?? null,
-                    null,
-                    $options['ip_address'] ?? null,
-                    $options['device_info'] ?? null
-                );
-
-                // 删除用户物品记录
-                $userItem->delete();
-            } else {
-                // 当前堆叠数量足够,部分消耗
-                $consumedQuantity = $remainingQuantity;
-                $userItem->quantity -= $consumedQuantity;
-                $userItem->save();
-                $remainingQuantity = 0;
-
-                // 记录交易日志
-                $this->logTransaction(
-                    $userId,
-                    $itemId,
-                    null,
-                    -$consumedQuantity,
-                    TRANSACTION_TYPE::CONSUME,
-                    $sourceType,
-                    $sourceId,
-                    $options['details'] ?? null,
-                    null,
-                    $options['ip_address'] ?? null,
-                    $options['device_info'] ?? null
-                );
-            }
+        // 获取物品信息
+        $item = ItemItem::findOrFail($itemId);
 
-            // 触发物品消耗事件
-            Event::dispatch(new ItemConsumed($userId, $itemId, null, $consumedQuantity, $options));
+        if ($instanceId) {
+            // 消耗单独属性物品
+            return $this->itemLogic->consumeUniqueItem($userId, $itemId, $instanceId, $options);
+        } else {
+            // 消耗统一属性物品
+            return $this->itemLogic->consumeNormalItem($userId, $itemId, $quantity, $options);
         }
-
-        return [
-            'success' => true,
-            'item_id' => $itemId,
-            'quantity' => $quantity,
-            'remaining_quantity' => $totalQuantity - $quantity,
-        ];
     }
 
-    /**
-     * 消耗单独属性物品
-     *
-     * @param int $userId 用户ID
-     * @param int $itemId 物品ID
-     * @param int $instanceId 物品实例ID
-     * @param array $options 选项
-     * @return array 消耗结果
-     * @throws Exception
-     */
-    protected function consumeUniqueItem(int $userId, int $itemId, int $instanceId, array $options = []): array
-    {
-        // 获取用户物品
-        $userItem = ItemUser::where('user_id', $userId)
-            ->where('item_id', $itemId)
-            ->where('instance_id', $instanceId)
-            ->first();
-
-        if (!$userItem) {
-            throw new Exception("用户 {$userId} 没有物品实例 {$instanceId}");
-        }
-
-        // 获取来源信息
-        $sourceType = $options['source_type'] ?? null;
-        $sourceId = $options['source_id'] ?? null;
 
-        // 记录交易日志
-        $this->logTransaction(
-            $userId,
-            $itemId,
-            $instanceId,
-            -1,
-            TRANSACTION_TYPE::CONSUME,
-            $sourceType,
-            $sourceId,
-            $options['details'] ?? null,
-            null,
-            $options['ip_address'] ?? null,
-            $options['device_info'] ?? null
-        );
-
-        // 删除用户物品记录
-        $userItem->delete();
-
-        // 是否删除物品实例
-        if (!empty($options['delete_instance'])) {
-            ItemInstance::where('id', $instanceId)->delete();
-        }
-
-        // 触发物品消耗事件
-        Event::dispatch(new ItemConsumed($userId, $itemId, $instanceId, 1, $options));
-
-        return [
-            'success' => true,
-            'item_id' => $itemId,
-            'instance_id' => $instanceId,
-        ];
-    }
-
-    /**
-     * 记录物品交易日志
-     *
-     * @param int $userId 用户ID
-     * @param int $itemId 物品ID
-     * @param int|null $instanceId 物品实例ID
-     * @param int $quantity 数量
-     * @param int $transactionType 交易类型
-     * @param string|null $sourceType 来源类型
-     * @param int|null $sourceId 来源ID
-     * @param array|null $details 详细信息
-     * @param string|null $expireAt 过期时间
-     * @param string|null $ipAddress IP地址
-     * @param string|null $deviceInfo 设备信息
-     * @return ItemTransactionLog
-     */
-    public function logTransaction(
-        int $userId,
-        int $itemId,
-        ?int $instanceId,
-        int $quantity,
-        int $transactionType,
-        ?string $sourceType = null,
-        ?int $sourceId = null,
-        ?array $details = null,
-        ?string $expireAt = null,
-        ?string $ipAddress = null,
-        ?string $deviceInfo = null
-    ): ItemTransactionLog {
-        return ItemTransactionLog::create([
-            'user_id' => $userId,
-            'item_id' => $itemId,
-            'instance_id' => $instanceId,
-            'quantity' => $quantity,
-            'transaction_type' => $transactionType,
-            'source_type' => $sourceType,
-            'source_id' => $sourceId,
-            'details' => $details,
-            'expire_at' => $expireAt,
-            'ip_address' => $ipAddress,
-            'device_info' => $deviceInfo,
-        ]);
-    }
 }