From 1d1f8901aaffe32f0b07d2e234a55489b7f42212 Mon Sep 17 00:00:00 2001 From: kkfluous Date: Thu, 16 Apr 2026 21:34:05 +0800 Subject: [PATCH] fix(scheduling): exclude near-qualified vehicles from rescue candidates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For rescue_hopeless (换走) scenario, filter out inventory candidates where totalMileage/yearTarget >= 80%. These are already near target and swapping them in adds no value. Instead, prioritize candidates with biggest mileage gaps — they benefit most from accumulating any mileage, even at a low-mileage customer. Before: showed 粤AGR6869 (93% done, 缺口 1990) as "可达标" — pointless After: shows 浙FF58720 (0% done, 缺口 60000) — genuinely needs mileage Co-Authored-By: Claude Opus 4.6 (1M context) --- src/server/routes/scheduling/algorithm.ts | 24 ++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/server/routes/scheduling/algorithm.ts b/src/server/routes/scheduling/algorithm.ts index 9961556..815bf53 100644 --- a/src/server/routes/scheduling/algorithm.ts +++ b/src/server/routes/scheduling/algorithm.ts @@ -76,13 +76,21 @@ export function generateSuggestions( const suggestions: SchedulingSuggestion[] = []; // --- rescue_hopeless (high priority) --- + // For this scenario: take the hopeless car away to a high-mileage customer, + // and give the low-mileage customer a replacement from inventory. + // Exclude near-qualified candidates (completionRate >= 80%) — no point swapping + // in a car that's basically already at target. + // Instead, pick cars with BIG gaps: they benefit from any mileage, even at a low customer. for (const vehicle of hopeless) { const candidates: CandidateVehicle[] = inventoryVehicles - .filter( - (inv) => - isTypeCompatible(vehicle.vehicleType, inv.vehicleType) && - inv.region === vehicle.region, - ) + .filter((inv) => { + if (!isTypeCompatible(vehicle.vehicleType, inv.vehicleType)) return false; + if (inv.region !== vehicle.region) return false; + // Exclude near-qualified: yearTarget known and already >= 80% done + const effectiveTarget = inv.yearTarget ?? vehicle.yearTarget; + if (effectiveTarget > 0 && inv.totalMileage / effectiveTarget >= 0.8) return false; + return true; + }) .map((inv) => { const effectiveTarget = inv.yearTarget ?? vehicle.yearTarget; const mileageGap = Math.max(0, effectiveTarget - inv.totalMileage); @@ -105,10 +113,8 @@ export function generateSuggestions( }; }) .sort((a, b) => { - if (a.canQualifyAfterSwap !== b.canQualifyAfterSwap) - return a.canQualifyAfterSwap ? -1 : 1; - // For hopeless: prefer already-qualified inventory, then highest completion - return b.completionRate - a.completionRate; + // Prefer biggest gap first — these benefit most from any mileage + return b.mileageGap - a.mileageGap; }) .slice(0, 5);