> $members * @param int|null $draw 测试用固定抽签值;生产环境留空并使用 random_int。 * @return array{selected_id:int,members:list>,eligible_count:int,total_weight:int} */ public static function select(array $members, string $today, int $now, ?int $draw = null): array { $eligibleIndexes = []; $totalWeight = 0; foreach ($members as $index => &$member) { if ((string) ($member['today_date'] ?? '') !== $today) { $member['today_date'] = $today; $member['today_count'] = 0; } // current_weight 是旧版平滑轮询游标;随机模式不再使用,统一归零。 $member['current_weight'] = 0; if (!self::eligible($member, $now)) { continue; } $weight = max(1, (int) ($member['weight'] ?? 1)); $eligibleIndexes[] = ['index' => $index, 'weight' => $weight]; $totalWeight += $weight; } unset($member); if ($eligibleIndexes === []) { return [ 'selected_id' => 0, 'members' => array_values($members), 'eligible_count' => 0, 'total_weight' => 0, ]; } $ticket = $draw ?? random_int(1, $totalWeight); if ($ticket < 1 || $ticket > $totalWeight) { throw new InvalidArgumentException('加权随机抽签值超出有效范围'); } $cursor = 0; $selectedIndex = (int) $eligibleIndexes[0]['index']; foreach ($eligibleIndexes as $candidate) { $cursor += (int) $candidate['weight']; if ($ticket <= $cursor) { $selectedIndex = (int) $candidate['index']; break; } } return [ 'selected_id' => (int) ($members[$selectedIndex]['id'] ?? 0), 'members' => array_values($members), 'eligible_count' => count($eligibleIndexes), 'total_weight' => $totalWeight, ]; } /** @param array $member */ public static function eligible(array $member, int $now): bool { if ((int) ($member['enabled'] ?? 0) !== 1 || (int) ($member['weight'] ?? 0) <= 0) { return false; } $start = max(0, (int) ($member['active_start'] ?? 0)); $end = max(0, (int) ($member['active_end'] ?? 0)); if (($start > 0 && $start > $now) || ($end > 0 && $end < $now)) { return false; } $limit = max(0, (int) ($member['daily_limit'] ?? 0)); return $limit === 0 || (int) ($member['today_count'] ?? 0) < $limit; } }