update pairing engine
WMS API/PXG_2026_API/pipeline/head There was a failure building this commit
Details
WMS API/PXG_2026_API/pipeline/head There was a failure building this commit
Details
This commit is contained in:
parent
62f96023e9
commit
4c1fa6cf21
|
|
@ -102,6 +102,108 @@ class PairingEngine
|
|||
}
|
||||
}
|
||||
|
||||
// 1b) Group-of-2/3 placement (keep same course; prefer same flight)
|
||||
if ($groupIds->isNotEmpty()) {
|
||||
$groups23 = Group::whereIn('id', $groupIds)->whereIn('size_target', [2,3])->get();
|
||||
|
||||
foreach ($groups23 as $g) {
|
||||
$need = (int) $g->size_target;
|
||||
|
||||
$groupRegs = Registration::where('group_id', $g->id)
|
||||
->where('status', 'CONFIRMED')
|
||||
->whereDoesntHave('flightMember')
|
||||
->with('player')
|
||||
->get();
|
||||
|
||||
// Only place when exact members exist (2 or 3)
|
||||
if ($groupRegs->count() !== $need) continue;
|
||||
|
||||
// derive coursePref from leader if any
|
||||
$coursePref = 'ANY';
|
||||
$leader = $groupRegs->firstWhere('type', 'GROUP_LEADER') ?? $groupRegs->first();
|
||||
if ($leader && in_array($leader->course_pref, ['A','B'])) $coursePref = $leader->course_pref;
|
||||
|
||||
// 1) Try same flight with enough seats
|
||||
$flightId = $this->findFlightWithAtLeastSeats($flights, $flightSeats, $courseLoad, $coursePref, $need);
|
||||
if (!$flightId && $coursePref !== 'ANY') {
|
||||
$flightId = $this->findFlightWithAtLeastSeats($flights, $flightSeats, $courseLoad, 'ANY', $need);
|
||||
}
|
||||
|
||||
if ($flightId) {
|
||||
$seats = array_slice($flightSeats[$flightId], 0, $need);
|
||||
|
||||
for ($i=0; $i<$need; $i++) {
|
||||
$r = $groupRegs[$i];
|
||||
|
||||
FlightMember::create([
|
||||
'flight_id' => $flightId,
|
||||
'registration_id' => $r->id,
|
||||
'seat_no' => $seats[$i],
|
||||
'lock_flag' => false,
|
||||
]);
|
||||
|
||||
$assigned++;
|
||||
$courseLoad[$flights[$flightId]->course] = ($courseLoad[$flights[$flightId]->course] ?? 0) + 1;
|
||||
|
||||
if ($this->isHighLike($r->handicap_band)) {
|
||||
$h = (int)$flights[$flightId]->start_hole;
|
||||
$holeHighLikeLoad[$h] = ($holeHighLikeLoad[$h] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// consume seats + status
|
||||
$flightSeats[$flightId] = array_values(array_slice($flightSeats[$flightId], $need));
|
||||
$this->refreshFlightStatus($flightId);
|
||||
$notes[] = "group {$g->group_code} -> flight {$flights[$flightId]->code}";
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2) If no single flight can fit, split but keep SAME COURSE
|
||||
$course = $this->pickCourseForGroupSplit($flights, $flightSeats, $courseLoad, $coursePref, $need);
|
||||
if (!$course) {
|
||||
$notes[] = "no seats available to place group {$g->group_code}";
|
||||
continue;
|
||||
}
|
||||
|
||||
$placed = 0;
|
||||
foreach ($flights as $f) {
|
||||
if ($f->course !== $course) continue;
|
||||
if (empty($flightSeats[$f->id] ?? [])) continue;
|
||||
|
||||
while (!empty($flightSeats[$f->id]) && $placed < $need) {
|
||||
$seat = array_shift($flightSeats[$f->id]);
|
||||
$r = $groupRegs[$placed];
|
||||
|
||||
FlightMember::create([
|
||||
'flight_id' => $f->id,
|
||||
'registration_id' => $r->id,
|
||||
'seat_no' => $seat,
|
||||
'lock_flag' => false,
|
||||
]);
|
||||
|
||||
$assigned++;
|
||||
$courseLoad[$course] = ($courseLoad[$course] ?? 0) + 1;
|
||||
|
||||
if ($this->isHighLike($r->handicap_band)) {
|
||||
$h = (int)$f->start_hole;
|
||||
$holeHighLikeLoad[$h] = ($holeHighLikeLoad[$h] ?? 0) + 1;
|
||||
}
|
||||
|
||||
$this->refreshFlightStatus($f->id);
|
||||
$placed++;
|
||||
}
|
||||
|
||||
if ($placed >= $need) break;
|
||||
}
|
||||
|
||||
if ($placed >= $need) {
|
||||
$notes[] = "group {$g->group_code} -> split in course {$course}";
|
||||
} else {
|
||||
$notes[] = "group {$g->group_code} placement incomplete (placed {$placed}/{$need})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// refresh remaining regs
|
||||
$regs = $getPendingRegs();
|
||||
|
||||
|
|
@ -481,4 +583,64 @@ class PairingEngine
|
|||
foreach (['LOW','MID'] as $b) if (!empty($pool[$b])) return array_shift($pool[$b]);
|
||||
return null;
|
||||
}
|
||||
private function findFlightWithAtLeastSeats($flights, array $flightSeats, array $courseLoad, string $coursePref, int $need): ?int
|
||||
{
|
||||
$candidates = [];
|
||||
foreach ($flights as $f) {
|
||||
$avail = count($flightSeats[$f->id] ?? []);
|
||||
if ($avail < $need) continue;
|
||||
|
||||
if (in_array($coursePref, ['A','B']) && $f->course !== $coursePref) continue;
|
||||
|
||||
// prefer less-filled flights; if ANY, keep course balanced
|
||||
$count = $f->members->count();
|
||||
$score = (4 - $count) * 40;
|
||||
|
||||
if (config('pxg.pairing.course_balance', true) && $coursePref === 'ANY') {
|
||||
$score += (200 - ($courseLoad[$f->course] ?? 0));
|
||||
}
|
||||
|
||||
$candidates[] = [$f->id, $score];
|
||||
}
|
||||
|
||||
if (empty($candidates)) return null;
|
||||
|
||||
usort($candidates, fn($x,$y) => $y[1] <=> $x[1]);
|
||||
return $candidates[0][0];
|
||||
}
|
||||
|
||||
private function pickCourseForGroupSplit($flights, array $flightSeats, array $courseLoad, string $coursePref, int $need): ?string
|
||||
{
|
||||
// if preferred course has enough total seats, choose it
|
||||
if (in_array($coursePref, ['A','B'])) {
|
||||
$total = 0;
|
||||
foreach ($flights as $f) {
|
||||
if ($f->course !== $coursePref) continue;
|
||||
$total += count($flightSeats[$f->id] ?? []);
|
||||
}
|
||||
if ($total >= $need) return $coursePref;
|
||||
}
|
||||
|
||||
// else choose course with more available seats (and/or lower load)
|
||||
$seatA = 0; $seatB = 0;
|
||||
foreach ($flights as $f) {
|
||||
$cnt = count($flightSeats[$f->id] ?? []);
|
||||
if ($f->course === 'A') $seatA += $cnt;
|
||||
if ($f->course === 'B') $seatB += $cnt;
|
||||
}
|
||||
|
||||
if ($seatA === 0 && $seatB === 0) return null;
|
||||
if ($seatA >= $need && $seatB < $need) return 'A';
|
||||
if ($seatB >= $need && $seatA < $need) return 'B';
|
||||
|
||||
// both can fit or neither fully fits -> keep balance (lower load), else more seats
|
||||
if (config('pxg.pairing.course_balance', true)) {
|
||||
$loadA = $courseLoad['A'] ?? 0;
|
||||
$loadB = $courseLoad['B'] ?? 0;
|
||||
return $loadA <= $loadB ? 'A' : 'B';
|
||||
}
|
||||
|
||||
return $seatA >= $seatB ? 'A' : 'B';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
"laravel/envoy": "^2.10",
|
||||
"laravel/pint": "^1.0",
|
||||
"laravel/sail": "^1.18",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "1c9becfc91324d2c5f77f109db0ebbab",
|
||||
"content-hash": "9c491b8531eec05ba41a11d9276a5749",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
|
|
@ -5816,69 +5816,6 @@
|
|||
},
|
||||
"time": "2025-04-30T06:54:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/envoy",
|
||||
"version": "v2.10.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/envoy.git",
|
||||
"reference": "819a519e3d86b056c7aa3bd5d0801952a6fc14fd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/envoy/zipball/819a519e3d86b056c7aa3bd5d0801952a6fc14fd",
|
||||
"reference": "819a519e3d86b056c7aa3bd5d0801952a6fc14fd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"guzzlehttp/guzzle": "^6.0|^7.0",
|
||||
"illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
|
||||
"php": "^7.2|^8.0",
|
||||
"symfony/console": "^4.3|^5.0|^6.0|^7.0",
|
||||
"symfony/process": "^4.3|^5.0|^6.0|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpunit/phpunit": "^8.0|^9.0|^10.4"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-posix": "Required to determine the System user on Unix systems."
|
||||
},
|
||||
"bin": [
|
||||
"bin/envoy"
|
||||
],
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Envoy\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "Elegant SSH tasks for PHP.",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"ssh"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/envoy/issues",
|
||||
"source": "https://github.com/laravel/envoy/tree/v2.10.2"
|
||||
},
|
||||
"time": "2025-01-28T15:47:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/pint",
|
||||
"version": "v1.20.0",
|
||||
|
|
|
|||
Loading…
Reference in New Issue