安全解冻是物品冻结系统中的核心功能,用于处理冻结物品被部分或完全消耗后的解冻操作。与普通解冻不同,安全解冻需要处理复杂的补足逻辑。
freezeLogId: 要解冻的冻结日志ID1. 检查事务状态
2. 查找冻结日志记录
3. 验证冻结日志的有效性
4. 查找对应的冻结物品堆
$originalFrozenQuantity = $freezeLog->quantity; // 原始冻结数量
$currentQuantity = $frozenItem->quantity; // 当前剩余数量
$shortageQuantity = $originalFrozenQuantity - $currentQuantity; // 需要补足的数量
if ($shortageQuantity == 0) {
// 冻结堆完整,直接解冻
return 正常解冻流程();
}
if ($shortageQuantity > 0) {
// 包含两种情况:
// 1. 部分消耗:currentQuantity > 0,需要补足部分差额
// 2. 完全消耗:currentQuantity <= 0,需要补足全部数量
// 两种情况的处理逻辑相同:都是从其他冻结堆中解冻来补足
return 补足解冻流程();
}
// 查找用户其他冻结物品(排除当前冻结堆)
$otherFrozenItems = ItemUser::where('user_id', $userId)
->where('item_id', $itemId)
->where('instance_id', $instanceId)
->where('is_frozen', true)
->where('frozen_log_id', '!=', $freezeLogId) // 排除当前冻结堆
->where('quantity', '>', 0)
->orderBy('expire_at') // 优先使用即将过期的
->lockForUpdate() // 锁定防并发
->get();
$totalOtherFrozenQuantity = $otherFrozenItems->sum('quantity');
if ($totalOtherFrozenQuantity < $shortageQuantity) {
throw new Exception("其他冻结数量不足以补足");
}
foreach ($otherFrozenItems as $otherFrozenItem) {
$unfreezeQuantity = min($otherFrozenItem->quantity, $remainingShortage);
// 从其他冻结堆中减少数量
$otherFrozenItem->quantity -= $unfreezeQuantity;
$otherFrozenItem->save();
// 记录解冻日志
ItemFreezeLog::createLog(
$userId, $itemId, $instanceId, $unfreezeQuantity,
FREEZE_ACTION_TYPE::UNFREEZE,
"补足解冻:从冻结堆{$otherFrozenItem->frozen_log_id}解冻{$unfreezeQuantity}个用于补足解冻日志{$freezeLogId}"
);
// 触发事件
event(new ItemQuantityChanged(...));
$remainingShortage -= $unfreezeQuantity;
if ($remainingShortage <= 0) break;
}
// 将目标冻结堆恢复到原始数量
$frozenItem->quantity = $originalFrozenQuantity;
$frozenItem->save();
// 创建解冻日志
$unfreezeLog = ItemFreezeLog::createLog(
$userId, $itemId, $instanceId, $originalFrozenQuantity,
FREEZE_ACTION_TYPE::UNFREEZE,
"安全解冻操作,原冻结日志ID: {$freezeLogId},补足差额: {$shortageQuantity}"
);
// 解冻物品
$frozenItem->is_frozen = false;
$frozenItem->frozen_log_id = null;
$frozenItem->save();
// 触发解冻事件
event(new ItemQuantityChanged(...));
throw new Exception("冻结日志 {$freezeLogId} 不存在");
throw new Exception("未找到冻结日志 {$freezeLogId} 对应的冻结物品");
throw new Exception("需要补足 {$shortageQuantity},但用户其他冻结数量只有 {$otherFrozenQuantity}");
throw new Exception("锁定后用户其他冻结数量只有 {$actualOtherFrozenQuantity}");
return [
'success' => true,
'user_id' => $userId,
'item_id' => $itemId,
'instance_id' => $instanceId,
'unfrozen_quantity' => $originalFrozenQuantity, // 实际解冻数量
'shortage_compensated' => $shortageQuantity, // 补足的差额
'user_item_id' => $frozenItem->id,
'unfreeze_log_id' => $unfreezeLog->id,
'compensation_details' => $unfreezeDetails, // 补足详情
];
lockForUpdate() 锁定相关记录| 特性 | 普通解冻 | 安全解冻 |
|---|---|---|
| 处理场景 | 冻结堆完整 | 冻结堆被消耗 |
| 补足机制 | 无需补足 | 从其他冻结堆补足 |
| 异常处理 | 严格抛异常 | 智能处理各种情况 |
| 返回信息 | 基础信息 | 详细补足信息 |
| 性能开销 | 较低 | 较高(需要查找和处理其他冻结堆) |