|
|
@@ -4,6 +4,7 @@ namespace App\Module\GameItems\Logics;
|
|
|
|
|
|
use App\Module\GameItems\Enums\FREEZE_ACTION_TYPE;
|
|
|
use App\Module\GameItems\Enums\FREEZE_REASON_TYPE;
|
|
|
+use App\Module\GameItems\Enums\TRANSACTION_TYPE;
|
|
|
use App\Module\GameItems\Events\ItemQuantityChanged;
|
|
|
use App\Module\GameItems\Models\Item;
|
|
|
use App\Module\GameItems\Models\ItemFreezeLog;
|
|
|
@@ -320,14 +321,141 @@ class ItemFreeze
|
|
|
throw new Exception("未找到冻结日志 {$freezeLogId} 对应的冻结物品");
|
|
|
}
|
|
|
|
|
|
- // 创建解冻日志
|
|
|
+ // 获取原始冻结数量和当前剩余数量
|
|
|
+ $originalFrozenQuantity = $freezeLog->quantity;
|
|
|
+ $currentQuantity = $frozenItem->quantity;
|
|
|
+
|
|
|
+ if ($currentQuantity <= 0) {
|
|
|
+ // 冻结堆已被完全消耗,无法解冻
|
|
|
+ throw new Exception("冻结日志 {$freezeLogId} 对应的冻结物品已被完全消耗,无法解冻");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否需要补足差额
|
|
|
+ $shortageQuantity = $originalFrozenQuantity - $currentQuantity;
|
|
|
+
|
|
|
+ if ($shortageQuantity > 0) {
|
|
|
+ // 冻结堆被部分消耗,需要从用户其他可用物品中补足
|
|
|
+ $availableQuantity = self::getAvailableQuantity(
|
|
|
+ $frozenItem->user_id,
|
|
|
+ $frozenItem->item_id,
|
|
|
+ $frozenItem->instance_id
|
|
|
+ );
|
|
|
+
|
|
|
+ if ($availableQuantity < $shortageQuantity) {
|
|
|
+ throw new Exception(
|
|
|
+ "解冻失败:原始冻结数量 {$originalFrozenQuantity},当前冻结剩余 {$currentQuantity}," .
|
|
|
+ "需要补足 {$shortageQuantity},但用户可用数量只有 {$availableQuantity}"
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从用户可用物品中扣除差额,补足到冻结堆
|
|
|
+ $availableItems = ItemUser::where('user_id', $frozenItem->user_id)
|
|
|
+ ->where('item_id', $frozenItem->item_id)
|
|
|
+ ->where('is_frozen', false)
|
|
|
+ ->whereNull('instance_id')
|
|
|
+ ->where('quantity', '>', 0)
|
|
|
+ ->orderBy('expire_at')
|
|
|
+ ->get();
|
|
|
+
|
|
|
+ $remainingShortage = $shortageQuantity;
|
|
|
+ $transferDetails = []; // 记录转移详情
|
|
|
+
|
|
|
+ foreach ($availableItems as $availableItem) {
|
|
|
+ if ($remainingShortage <= 0) break;
|
|
|
+
|
|
|
+ $deductQuantity = min($availableItem->quantity, $remainingShortage);
|
|
|
+ $oldQuantity = $availableItem->quantity;
|
|
|
+ $newQuantity = $oldQuantity - $deductQuantity;
|
|
|
+
|
|
|
+ // 更新可用物品数量
|
|
|
+ $availableItem->quantity = $newQuantity;
|
|
|
+ $availableItem->save();
|
|
|
+
|
|
|
+ // 记录转移详情
|
|
|
+ $transferDetails[] = [
|
|
|
+ 'from_user_item_id' => $availableItem->id,
|
|
|
+ 'transferred_quantity' => $deductQuantity,
|
|
|
+ 'old_quantity' => $oldQuantity,
|
|
|
+ 'new_quantity' => $newQuantity,
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 记录交易日志(从可用物品扣除)
|
|
|
+ \App\Module\GameItems\Logics\Item::logTransaction(
|
|
|
+ $frozenItem->user_id,
|
|
|
+ $frozenItem->item_id,
|
|
|
+ null,
|
|
|
+ -$deductQuantity,
|
|
|
+ TRANSACTION_TYPE::TRADE_OUT, // 使用交易失去类型
|
|
|
+ 'unfreeze_compensation',
|
|
|
+ $freezeLogId,
|
|
|
+ ["解冻补足转移:从可用物品转移 {$deductQuantity} 个到冻结堆"]
|
|
|
+ );
|
|
|
+
|
|
|
+ // 触发物品数量变更事件(可用物品减少)
|
|
|
+ event(new ItemQuantityChanged(
|
|
|
+ $frozenItem->user_id,
|
|
|
+ $frozenItem->item_id,
|
|
|
+ null,
|
|
|
+ $oldQuantity,
|
|
|
+ $newQuantity,
|
|
|
+ $availableItem->id,
|
|
|
+ false, // 旧冻结状态:未冻结
|
|
|
+ false, // 新冻结状态:未冻结
|
|
|
+ [
|
|
|
+ 'action' => 'unfreeze_compensation_transfer_out',
|
|
|
+ 'freeze_log_id' => $freezeLogId,
|
|
|
+ 'transferred_quantity' => $deductQuantity,
|
|
|
+ ]
|
|
|
+ ));
|
|
|
+
|
|
|
+ $remainingShortage -= $deductQuantity;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 将补足的数量加到冻结堆
|
|
|
+ $oldFrozenQuantity = $frozenItem->quantity;
|
|
|
+ $frozenItem->quantity = $originalFrozenQuantity;
|
|
|
+ $frozenItem->save();
|
|
|
+
|
|
|
+ // 记录交易日志(向冻结堆补足)
|
|
|
+ \App\Module\GameItems\Logics\Item::logTransaction(
|
|
|
+ $frozenItem->user_id,
|
|
|
+ $frozenItem->item_id,
|
|
|
+ $frozenItem->instance_id,
|
|
|
+ $shortageQuantity,
|
|
|
+ TRANSACTION_TYPE::TRADE_IN, // 使用交易获得类型
|
|
|
+ 'unfreeze_compensation',
|
|
|
+ $freezeLogId,
|
|
|
+ ["解冻补足转移:向冻结堆补足 {$shortageQuantity} 个,转移详情:" . json_encode($transferDetails)]
|
|
|
+ );
|
|
|
+
|
|
|
+ // 触发物品数量变更事件(冻结堆增加)
|
|
|
+ event(new ItemQuantityChanged(
|
|
|
+ $frozenItem->user_id,
|
|
|
+ $frozenItem->item_id,
|
|
|
+ $frozenItem->instance_id,
|
|
|
+ $oldFrozenQuantity,
|
|
|
+ $originalFrozenQuantity,
|
|
|
+ $frozenItem->id,
|
|
|
+ true, // 旧冻结状态:已冻结
|
|
|
+ true, // 新冻结状态:已冻结
|
|
|
+ [
|
|
|
+ 'action' => 'unfreeze_compensation_transfer_in',
|
|
|
+ 'freeze_log_id' => $freezeLogId,
|
|
|
+ 'compensated_quantity' => $shortageQuantity,
|
|
|
+ 'transfer_details' => $transferDetails,
|
|
|
+ ]
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建解冻日志,记录原始冻结数量
|
|
|
$unfreezeLog = ItemFreezeLog::createLog(
|
|
|
$frozenItem->user_id,
|
|
|
$frozenItem->item_id,
|
|
|
$frozenItem->instance_id,
|
|
|
- $frozenItem->quantity,
|
|
|
+ $originalFrozenQuantity, // 解冻原始数量
|
|
|
FREEZE_ACTION_TYPE::UNFREEZE,
|
|
|
- "解冻操作,原冻结日志ID: {$freezeLogId}",
|
|
|
+ "解冻操作,原冻结日志ID: {$freezeLogId},原始冻结数量: {$originalFrozenQuantity}" .
|
|
|
+ ($shortageQuantity > 0 ? ",补足差额: {$shortageQuantity}" : ""),
|
|
|
$freezeLog->source_id,
|
|
|
$freezeLog->source_type,
|
|
|
$freezeLog->operator_id
|
|
|
@@ -343,8 +471,8 @@ class ItemFreeze
|
|
|
$frozenItem->user_id,
|
|
|
$frozenItem->item_id,
|
|
|
$frozenItem->instance_id,
|
|
|
- $frozenItem->quantity, // 旧数量(数量未变)
|
|
|
- $frozenItem->quantity, // 新数量(数量未变)
|
|
|
+ $originalFrozenQuantity, // 旧数量(恢复到原始数量)
|
|
|
+ $originalFrozenQuantity, // 新数量(数量未变,只是解冻状态变更)
|
|
|
$frozenItem->id,
|
|
|
true, // 旧冻结状态:已冻结
|
|
|
false, // 新冻结状态:未冻结
|
|
|
@@ -352,6 +480,8 @@ class ItemFreeze
|
|
|
'freeze_action' => 'unfreeze',
|
|
|
'unfreeze_log_id' => $unfreezeLog->id,
|
|
|
'original_freeze_log_id' => $freezeLogId,
|
|
|
+ 'original_frozen_quantity' => $originalFrozenQuantity,
|
|
|
+ 'shortage_compensated' => $shortageQuantity,
|
|
|
'source_id' => $freezeLog->source_id,
|
|
|
'source_type' => $freezeLog->source_type,
|
|
|
'operator_id' => $freezeLog->operator_id,
|
|
|
@@ -363,7 +493,8 @@ class ItemFreeze
|
|
|
'user_id' => $frozenItem->user_id,
|
|
|
'item_id' => $frozenItem->item_id,
|
|
|
'instance_id' => $frozenItem->instance_id,
|
|
|
- 'unfrozen_quantity' => $frozenItem->quantity,
|
|
|
+ 'unfrozen_quantity' => $originalFrozenQuantity, // 返回原始冻结数量
|
|
|
+ 'shortage_compensated' => $shortageQuantity, // 返回补足的差额
|
|
|
'user_item_id' => $frozenItem->id,
|
|
|
'unfreeze_log_id' => $unfreezeLog->id,
|
|
|
];
|
|
|
@@ -626,4 +757,264 @@ class ItemFreeze
|
|
|
|
|
|
return $statistics;
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 安全解冻物品(处理已被消耗的冻结堆)
|
|
|
+ *
|
|
|
+ * 与unfreezeByLogId不同,此方法会检查冻结物品是否已被消耗:
|
|
|
+ * - 如果冻结物品数量为0,则标记为已处理,不抛出异常
|
|
|
+ * - 如果冻结物品数量大于0,则正常解冻
|
|
|
+ * - 返回详细的处理结果信息
|
|
|
+ *
|
|
|
+ * @param int $freezeLogId 冻结日志ID
|
|
|
+ * @return array 解冻结果
|
|
|
+ * @throws Exception
|
|
|
+ */
|
|
|
+ public static function safeUnfreezeByLogId(int $freezeLogId): array
|
|
|
+ {
|
|
|
+ // 检查事务
|
|
|
+ Helper::check_tr();
|
|
|
+
|
|
|
+ // 查找冻结日志
|
|
|
+ $freezeLog = ItemFreezeLog::find($freezeLogId);
|
|
|
+ if (!$freezeLog) {
|
|
|
+ throw new Exception("冻结日志 {$freezeLogId} 不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!$freezeLog->isFreeze()) {
|
|
|
+ throw new Exception("日志 {$freezeLogId} 不是冻结操作记录");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查找对应的冻结物品
|
|
|
+ $frozenItem = ItemUser::where('frozen_log_id', $freezeLogId)
|
|
|
+ ->where('is_frozen', true)
|
|
|
+ ->first();
|
|
|
+
|
|
|
+ if (!$frozenItem) {
|
|
|
+ // 冻结物品不存在,可能已被删除或处理
|
|
|
+ return [
|
|
|
+ 'success' => true,
|
|
|
+ 'status' => 'already_processed',
|
|
|
+ 'message' => "冻结日志 {$freezeLogId} 对应的冻结物品不存在,可能已被处理",
|
|
|
+ 'freeze_log_id' => $freezeLogId,
|
|
|
+ 'unfrozen_quantity' => 0,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取原始冻结数量和当前剩余数量
|
|
|
+ $originalFrozenQuantity = $freezeLog->quantity;
|
|
|
+ $currentQuantity = $frozenItem->quantity;
|
|
|
+
|
|
|
+ if ($currentQuantity <= 0) {
|
|
|
+ // 冻结堆已被完全消耗,尝试从用户其他可用物品中补足
|
|
|
+ $availableQuantity = self::getAvailableQuantity(
|
|
|
+ $frozenItem->user_id,
|
|
|
+ $frozenItem->item_id,
|
|
|
+ $frozenItem->instance_id
|
|
|
+ );
|
|
|
+
|
|
|
+ if ($availableQuantity < $originalFrozenQuantity) {
|
|
|
+ // 用户可用数量不足,无法完全解冻
|
|
|
+ return [
|
|
|
+ 'success' => false,
|
|
|
+ 'status' => 'insufficient_available',
|
|
|
+ 'message' => "冻结物品已被完全消耗,用户可用数量不足以补足原始冻结数量",
|
|
|
+ 'user_id' => $frozenItem->user_id,
|
|
|
+ 'item_id' => $frozenItem->item_id,
|
|
|
+ 'instance_id' => $frozenItem->instance_id,
|
|
|
+ 'unfrozen_quantity' => 0,
|
|
|
+ 'original_frozen_quantity' => $originalFrozenQuantity,
|
|
|
+ 'current_frozen_quantity' => $currentQuantity,
|
|
|
+ 'available_quantity' => $availableQuantity,
|
|
|
+ 'shortage_quantity' => $originalFrozenQuantity - $availableQuantity,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从用户可用物品中补足全部数量
|
|
|
+ $availableItems = ItemUser::where('user_id', $frozenItem->user_id)
|
|
|
+ ->where('item_id', $frozenItem->item_id)
|
|
|
+ ->where('is_frozen', false)
|
|
|
+ ->whereNull('instance_id')
|
|
|
+ ->where('quantity', '>', 0)
|
|
|
+ ->orderBy('expire_at')
|
|
|
+ ->get();
|
|
|
+
|
|
|
+ $remainingQuantity = $originalFrozenQuantity;
|
|
|
+ foreach ($availableItems as $availableItem) {
|
|
|
+ if ($remainingQuantity <= 0) break;
|
|
|
+
|
|
|
+ $deductQuantity = min($availableItem->quantity, $remainingQuantity);
|
|
|
+ $availableItem->quantity -= $deductQuantity;
|
|
|
+ $availableItem->save();
|
|
|
+
|
|
|
+ $remainingQuantity -= $deductQuantity;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 恢复冻结堆到原始数量
|
|
|
+ $frozenItem->quantity = $originalFrozenQuantity;
|
|
|
+ $frozenItem->save();
|
|
|
+
|
|
|
+ $shortageQuantity = $originalFrozenQuantity;
|
|
|
+ } else {
|
|
|
+ // 检查是否需要补足差额
|
|
|
+ $shortageQuantity = $originalFrozenQuantity - $currentQuantity;
|
|
|
+
|
|
|
+ if ($shortageQuantity > 0) {
|
|
|
+ // 冻结堆被部分消耗,需要从用户其他可用物品中补足
|
|
|
+ $availableQuantity = self::getAvailableQuantity(
|
|
|
+ $frozenItem->user_id,
|
|
|
+ $frozenItem->item_id,
|
|
|
+ $frozenItem->instance_id
|
|
|
+ );
|
|
|
+
|
|
|
+ if ($availableQuantity < $shortageQuantity) {
|
|
|
+ return [
|
|
|
+ 'success' => false,
|
|
|
+ 'status' => 'insufficient_available',
|
|
|
+ 'message' => "解冻失败:需要补足 {$shortageQuantity},但用户可用数量只有 {$availableQuantity}",
|
|
|
+ 'user_id' => $frozenItem->user_id,
|
|
|
+ 'item_id' => $frozenItem->item_id,
|
|
|
+ 'instance_id' => $frozenItem->instance_id,
|
|
|
+ 'unfrozen_quantity' => 0,
|
|
|
+ 'original_frozen_quantity' => $originalFrozenQuantity,
|
|
|
+ 'current_frozen_quantity' => $currentQuantity,
|
|
|
+ 'shortage_quantity' => $shortageQuantity,
|
|
|
+ 'available_quantity' => $availableQuantity,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从用户可用物品中扣除差额,补足到冻结堆
|
|
|
+ $availableItems = ItemUser::where('user_id', $frozenItem->user_id)
|
|
|
+ ->where('item_id', $frozenItem->item_id)
|
|
|
+ ->where('is_frozen', false)
|
|
|
+ ->whereNull('instance_id')
|
|
|
+ ->where('quantity', '>', 0)
|
|
|
+ ->orderBy('expire_at')
|
|
|
+ ->get();
|
|
|
+
|
|
|
+ $remainingShortage = $shortageQuantity;
|
|
|
+ foreach ($availableItems as $availableItem) {
|
|
|
+ if ($remainingShortage <= 0) break;
|
|
|
+
|
|
|
+ $deductQuantity = min($availableItem->quantity, $remainingShortage);
|
|
|
+ $availableItem->quantity -= $deductQuantity;
|
|
|
+ $availableItem->save();
|
|
|
+
|
|
|
+ $remainingShortage -= $deductQuantity;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 将补足的数量加到冻结堆
|
|
|
+ $frozenItem->quantity = $originalFrozenQuantity;
|
|
|
+ $frozenItem->save();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建解冻日志
|
|
|
+ $unfreezeLog = ItemFreezeLog::createLog(
|
|
|
+ $frozenItem->user_id,
|
|
|
+ $frozenItem->item_id,
|
|
|
+ $frozenItem->instance_id,
|
|
|
+ $originalFrozenQuantity,
|
|
|
+ FREEZE_ACTION_TYPE::UNFREEZE,
|
|
|
+ "安全解冻操作,原冻结日志ID: {$freezeLogId},原始冻结数量: {$originalFrozenQuantity}" .
|
|
|
+ ($shortageQuantity > 0 ? ",补足差额: {$shortageQuantity}" : ""),
|
|
|
+ $freezeLog->source_id,
|
|
|
+ $freezeLog->source_type,
|
|
|
+ $freezeLog->operator_id
|
|
|
+ );
|
|
|
+
|
|
|
+ // 解冻物品
|
|
|
+ $frozenItem->is_frozen = false;
|
|
|
+ $frozenItem->frozen_log_id = null;
|
|
|
+ $frozenItem->save();
|
|
|
+
|
|
|
+ // 触发物品变更事件
|
|
|
+ event(new ItemQuantityChanged(
|
|
|
+ $frozenItem->user_id,
|
|
|
+ $frozenItem->item_id,
|
|
|
+ $frozenItem->instance_id,
|
|
|
+ $originalFrozenQuantity,
|
|
|
+ $originalFrozenQuantity,
|
|
|
+ $frozenItem->id,
|
|
|
+ true, // 旧冻结状态:已冻结
|
|
|
+ false, // 新冻结状态:未冻结
|
|
|
+ [
|
|
|
+ 'freeze_action' => 'safe_unfreeze',
|
|
|
+ 'unfreeze_log_id' => $unfreezeLog->id,
|
|
|
+ 'original_freeze_log_id' => $freezeLogId,
|
|
|
+ 'original_frozen_quantity' => $originalFrozenQuantity,
|
|
|
+ 'shortage_compensated' => $shortageQuantity,
|
|
|
+ 'source_id' => $freezeLog->source_id,
|
|
|
+ 'source_type' => $freezeLog->source_type,
|
|
|
+ 'operator_id' => $freezeLog->operator_id,
|
|
|
+ ]
|
|
|
+ ));
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'success' => true,
|
|
|
+ 'status' => 'unfrozen',
|
|
|
+ 'message' => "成功解冻物品",
|
|
|
+ 'user_id' => $frozenItem->user_id,
|
|
|
+ 'item_id' => $frozenItem->item_id,
|
|
|
+ 'instance_id' => $frozenItem->instance_id,
|
|
|
+ 'unfrozen_quantity' => $originalFrozenQuantity,
|
|
|
+ 'shortage_compensated' => $shortageQuantity,
|
|
|
+ 'user_item_id' => $frozenItem->id,
|
|
|
+ 'unfreeze_log_id' => $unfreezeLog->id,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取已被消耗的冻结物品统计
|
|
|
+ *
|
|
|
+ * 查找所有数量为0的冻结物品记录并返回统计信息
|
|
|
+ * 注意:不删除记录,只提供统计信息
|
|
|
+ *
|
|
|
+ * @param int|null $userId 用户ID,为null时统计所有用户
|
|
|
+ * @return array 统计结果
|
|
|
+ */
|
|
|
+ public static function getConsumedFrozenItemsStatistics(?int $userId = null): array
|
|
|
+ {
|
|
|
+ $query = ItemUser::where('is_frozen', true)
|
|
|
+ ->where('quantity', '<=', 0);
|
|
|
+
|
|
|
+ if ($userId) {
|
|
|
+ $query->where('user_id', $userId);
|
|
|
+ }
|
|
|
+
|
|
|
+ $consumedFrozenItems = $query->get();
|
|
|
+
|
|
|
+ $statistics = [
|
|
|
+ 'total_count' => $consumedFrozenItems->count(),
|
|
|
+ 'by_user' => [],
|
|
|
+ 'by_item' => [],
|
|
|
+ 'items' => [],
|
|
|
+ ];
|
|
|
+
|
|
|
+ foreach ($consumedFrozenItems as $frozenItem) {
|
|
|
+ // 按用户统计
|
|
|
+ if (!isset($statistics['by_user'][$frozenItem->user_id])) {
|
|
|
+ $statistics['by_user'][$frozenItem->user_id] = 0;
|
|
|
+ }
|
|
|
+ $statistics['by_user'][$frozenItem->user_id]++;
|
|
|
+
|
|
|
+ // 按物品统计
|
|
|
+ if (!isset($statistics['by_item'][$frozenItem->item_id])) {
|
|
|
+ $statistics['by_item'][$frozenItem->item_id] = 0;
|
|
|
+ }
|
|
|
+ $statistics['by_item'][$frozenItem->item_id]++;
|
|
|
+
|
|
|
+ // 详细记录
|
|
|
+ $statistics['items'][] = [
|
|
|
+ 'user_id' => $frozenItem->user_id,
|
|
|
+ 'item_id' => $frozenItem->item_id,
|
|
|
+ 'instance_id' => $frozenItem->instance_id,
|
|
|
+ 'user_item_id' => $frozenItem->id,
|
|
|
+ 'frozen_log_id' => $frozenItem->frozen_log_id,
|
|
|
+ 'quantity' => $frozenItem->quantity,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ return $statistics;
|
|
|
+ }
|
|
|
}
|