updateStatus($draw, $number_4d, true, 'close', 'admin_manual_close'); if ($pool === null) { return ApiMessage::errorResponse($request, 'not_found', ErrorCode::ClientHttpError->value, null, 404); } return ApiResponse::success($this->row($pool)); } public function recover(Request $request, Draw $draw, string $number_4d): JsonResponse { $pool = $this->updateStatus($draw, $number_4d, false, 'recover', 'admin_manual_recover'); if ($pool === null) { return ApiMessage::errorResponse($request, 'not_found', ErrorCode::ClientHttpError->value, null, 404); } if ((int) $pool->remaining_amount <= 0) { return ApiMessage::errorResponse($request, 'risk_pool_no_remaining_amount', ErrorCode::ClientHttpError->value, [ 'reason' => 'risk_pool_no_remaining_amount', ], 409); } return ApiResponse::success($this->row($pool)); } private function updateStatus( Draw $draw, string $number4d, bool $soldOut, string $actionType, string $reason, ): ?RiskPool { return DB::transaction(function () use ($draw, $number4d, $soldOut, $actionType, $reason): ?RiskPool { $pool = RiskPool::query() ->where('draw_id', $draw->id) ->where('normalized_number', $number4d) ->lockForUpdate() ->first(); if ($pool === null) { return null; } if (! $soldOut && (int) $pool->remaining_amount <= 0) { return $pool; } $targetStatus = $soldOut ? 1 : 0; $soldOutBefore = (int) $pool->sold_out_status; if ($soldOutBefore !== $targetStatus) { $pool->forceFill([ 'sold_out_status' => $targetStatus, 'version' => (int) $pool->version + 1, ])->save(); if ($targetStatus === 1) { $this->riskPoolService->publishManualSoldOut($draw, $number4d); } RiskPoolLockLog::query()->create([ 'draw_id' => $draw->id, 'normalized_number' => $number4d, 'ticket_item_id' => null, 'action_type' => $actionType, 'amount' => 0, 'source_reason' => $reason, 'created_at' => now(), ]); } $fresh = $pool->fresh(); if ($fresh !== null) { $this->riskPoolService->syncRedisStateFromPool($fresh); } return $fresh; }); } /** @return array */ private function row(RiskPool $pool): array { $cap = (int) $pool->total_cap_amount; $locked = (int) $pool->locked_amount; return [ 'normalized_number' => $pool->normalized_number, 'total_cap_amount' => $cap, 'locked_amount' => $locked, 'remaining_amount' => (int) $pool->remaining_amount, 'sold_out_status' => (int) $pool->sold_out_status, 'is_sold_out' => (int) $pool->sold_out_status === 1, 'usage_ratio' => $cap > 0 ? round($locked / $cap, 6) : null, 'version' => (int) $pool->version, ]; } }