user(); // 批次是否在领取有效期内。 if (! $run->isClaimable()) { return response()->json(['ok' => false, 'message' => '活动已结束或已过期。']); } return DB::transaction(function () use ($run, $user): JsonResponse { /** @var HolidayEventRun|null $lockedRun */ $lockedRun = HolidayEventRun::query() ->whereKey($run->id) ->lockForUpdate() ->first(); if (! $lockedRun || ! $lockedRun->isClaimable()) { return response()->json(['ok' => false, 'message' => '活动已结束或已过期。']); } /** @var HolidayClaim|null $claim */ $claim = HolidayClaim::query() ->where('run_id', $lockedRun->id) ->where('user_id', $user->id) ->lockForUpdate() ->first(); if (! $claim) { return response()->json(['ok' => false, 'message' => '您不在本次福利名单内,或活动已结束。']); } // claimed_at 不为空代表本轮已领过,直接返回幂等提示。 if ($claim->claimed_at !== null) { return response()->json([ 'ok' => false, 'message' => '您已领取过本轮福利。', 'amount' => $claim->amount, ]); } // 金币入账。 $this->currency->change( $user, 'gold', $claim->amount, CurrencySource::HOLIDAY_BONUS, "节日福利:{$lockedRun->event_name}", ); // 领取成功后只更新 claimed_at,不再删除记录,便于幂等和历史追踪。 $claim->update(['claimed_at' => now()]); // 批次领取统计按成功领取次数累计。 $lockedRun->increment('claimed_count'); $lockedRun->increment('claimed_amount', $claim->amount); $remainingPendingClaims = HolidayClaim::query() ->where('run_id', $lockedRun->id) ->whereNull('claimed_at') ->count(); if ($remainingPendingClaims === 0) { $lockedRun->update(['status' => 'completed']); } return response()->json([ 'ok' => true, 'message' => "🎉 恭喜!已领取 {$claim->amount} 金币!", 'amount' => $claim->amount, ]); }); } /** * 查询当前用户在指定批次中的待领取状态。 */ public function status(Request $request, HolidayEventRun $run): JsonResponse { $user = $request->user(); $claim = HolidayClaim::query() ->where('run_id', $run->id) ->where('user_id', $user->id) ->first(); return response()->json([ 'claimable' => $claim !== null && $claim->claimed_at === null && $run->isClaimable(), 'claimed' => $claim?->claimed_at !== null, 'amount' => $claim?->amount ?? 0, 'status' => $run->status, 'expires_at' => $run->expires_at?->toIso8601String(), ]); } }