fix(scheduling): exclude near-qualified vehicles from rescue candidates
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) <noreply@anthropic.com>
This commit is contained in:
@@ -76,13 +76,21 @@ export function generateSuggestions(
|
|||||||
const suggestions: SchedulingSuggestion[] = [];
|
const suggestions: SchedulingSuggestion[] = [];
|
||||||
|
|
||||||
// --- rescue_hopeless (high priority) ---
|
// --- 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) {
|
for (const vehicle of hopeless) {
|
||||||
const candidates: CandidateVehicle[] = inventoryVehicles
|
const candidates: CandidateVehicle[] = inventoryVehicles
|
||||||
.filter(
|
.filter((inv) => {
|
||||||
(inv) =>
|
if (!isTypeCompatible(vehicle.vehicleType, inv.vehicleType)) return false;
|
||||||
isTypeCompatible(vehicle.vehicleType, inv.vehicleType) &&
|
if (inv.region !== vehicle.region) return false;
|
||||||
inv.region === vehicle.region,
|
// 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) => {
|
.map((inv) => {
|
||||||
const effectiveTarget = inv.yearTarget ?? vehicle.yearTarget;
|
const effectiveTarget = inv.yearTarget ?? vehicle.yearTarget;
|
||||||
const mileageGap = Math.max(0, effectiveTarget - inv.totalMileage);
|
const mileageGap = Math.max(0, effectiveTarget - inv.totalMileage);
|
||||||
@@ -105,10 +113,8 @@ export function generateSuggestions(
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (a.canQualifyAfterSwap !== b.canQualifyAfterSwap)
|
// Prefer biggest gap first — these benefit most from any mileage
|
||||||
return a.canQualifyAfterSwap ? -1 : 1;
|
return b.mileageGap - a.mileageGap;
|
||||||
// For hopeless: prefer already-qualified inventory, then highest completion
|
|
||||||
return b.completionRate - a.completionRate;
|
|
||||||
})
|
})
|
||||||
.slice(0, 5);
|
.slice(0, 5);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user