|
|
@@ -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,
|
|
|
- ]);
|
|
|
- }
|
|
|
}
|