updates on the order bulk fix
This commit is contained in:
@@ -68,9 +68,16 @@ func maxActiveBookings() int64 {
|
||||
}
|
||||
|
||||
type milerCandidate struct {
|
||||
profile models.MilerProfile
|
||||
distanceKm float64
|
||||
profile models.MilerProfile
|
||||
distanceKm float64
|
||||
// activeBookings is the rider's load "in hand": today's open orders that
|
||||
// are not cancelled (openStopsToday). Balancing gives the next order to
|
||||
// the rider with the fewest; it is also what the ceiling caps.
|
||||
activeBookings int64
|
||||
// sessionStops counts orders given to the rider since they started duty —
|
||||
// the first tie-break, so a rider who just came online is not passed over
|
||||
// for one who has been busy all day.
|
||||
sessionStops int64
|
||||
}
|
||||
|
||||
// AssignCRMMiler finds the best available nearby miler for an express booking
|
||||
@@ -144,14 +151,17 @@ func tryAssign(bookingID int) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
candidate, agentDecisionID, found := selectMilerWithAI(&booking, nearby)
|
||||
candidate, pool, agentDecisionID, found := selectMilerWithAI(&booking, nearby)
|
||||
if !found {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if err := commitAssignment(&booking, candidate, agentDecisionID); err != nil {
|
||||
if errors.Is(err, errBookingTaken) {
|
||||
if _, err := commitAssignment(&booking, candidate, pool, agentDecisionID); err != nil {
|
||||
switch {
|
||||
case errors.Is(err, errBookingTaken):
|
||||
return true, nil
|
||||
case errors.Is(err, errNoRiderCapacity):
|
||||
return false, nil // everyone filled up meanwhile; retry later
|
||||
}
|
||||
return false, fmt.Errorf("commit: %w", err)
|
||||
}
|
||||
@@ -200,7 +210,7 @@ func TryAssignOnce(bookingID int) (AutoAssignResult, error) {
|
||||
return AutoAssignResult{Escalated: true, Reasoning: "no milers within search radius", SearchedRadiusKm: geoRadiusKm}, nil
|
||||
}
|
||||
|
||||
candidate, agentDecisionID, found := selectMilerWithAI(&booking, nearby)
|
||||
candidate, pool, agentDecisionID, found := selectMilerWithAI(&booking, nearby)
|
||||
if !found {
|
||||
return AutoAssignResult{
|
||||
Escalated: true,
|
||||
@@ -210,12 +220,25 @@ func TryAssignOnce(bookingID int) (AutoAssignResult, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
if err := commitAssignment(&booking, candidate, agentDecisionID); err != nil {
|
||||
if errors.Is(err, errBookingTaken) {
|
||||
chosenID := candidate.profile.Userid
|
||||
candidate, err = commitAssignment(&booking, candidate, pool, agentDecisionID)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, errBookingTaken):
|
||||
return AutoAssignResult{Assigned: true}, nil
|
||||
case errors.Is(err, errNoRiderCapacity):
|
||||
return AutoAssignResult{
|
||||
Escalated: true,
|
||||
Reasoning: "every nearby rider is at the order limit",
|
||||
SearchedRadiusKm: geoRadiusKm,
|
||||
CandidatesFound: len(nearby),
|
||||
}, nil
|
||||
}
|
||||
return AutoAssignResult{}, fmt.Errorf("commit: %w", err)
|
||||
}
|
||||
if candidate.profile.Userid != chosenID {
|
||||
agentDecisionID = nil // balancing overrode the AI's pick; its reasoning no longer applies
|
||||
}
|
||||
|
||||
reasoning := ""
|
||||
if agentDecisionID != nil {
|
||||
@@ -252,11 +275,23 @@ func queryNearbyMilers(lat, lon float64) ([]redis.GeoLocation, error) {
|
||||
|
||||
// commitAssignment writes the BookingAssignment row, updates the booking and the
|
||||
// miler's availability status in a single transaction, then publishes to NATS.
|
||||
func commitAssignment(booking *models.PickupBooking, candidate *milerCandidate, agentDecisionID *uint64) error {
|
||||
milerUserID := candidate.profile.Userid
|
||||
|
||||
func commitAssignment(booking *models.PickupBooking, candidate *milerCandidate, pool []*milerCandidate, agentDecisionID *uint64) (*milerCandidate, error) {
|
||||
tx := db.DB.Begin()
|
||||
|
||||
// Re-check the choice with fresh loads under the decision lock, so a burst
|
||||
// of orders spreads across riders instead of all landing on the one that
|
||||
// looked least loaded when each attempt started. See finalizeChoice.
|
||||
final, err := finalizeChoice(tx, candidate, pool)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
if final != candidate {
|
||||
agentDecisionID = nil
|
||||
}
|
||||
candidate = final
|
||||
milerUserID := candidate.profile.Userid
|
||||
|
||||
// Claim the booking first, and only if it is still unassigned and not
|
||||
// cancelled — see claimBooking.
|
||||
if err := claimBooking(tx, booking.Bookingid, map[string]interface{}{
|
||||
@@ -265,7 +300,7 @@ func commitAssignment(booking *models.PickupBooking, candidate *milerCandidate,
|
||||
"updatedat": time.Now(),
|
||||
}); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
assignment := models.BookingAssignment{
|
||||
@@ -274,17 +309,18 @@ func commitAssignment(booking *models.PickupBooking, candidate *milerCandidate,
|
||||
Assignmentstatus: constants.AssignmentAssigned,
|
||||
Assignedat: time.Now(),
|
||||
AgentDecisionID: agentDecisionID,
|
||||
Remarks: autoAssignedRemark,
|
||||
}
|
||||
if err := tx.Create(&assignment).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return fmt.Errorf("create BookingAssignment: %w", err)
|
||||
return nil, fmt.Errorf("create BookingAssignment: %w", err)
|
||||
}
|
||||
|
||||
if err := tx.Model(&models.MilerProfile{}).
|
||||
Where("userid = ?", milerUserID).
|
||||
Update("availabilitystatus", constants.MilerAssigned).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return fmt.Errorf("update MilerProfile availability: %w", err)
|
||||
return nil, fmt.Errorf("update MilerProfile availability: %w", err)
|
||||
}
|
||||
|
||||
// The customer's "Miler assigned" milestone, in the same transaction as the
|
||||
@@ -298,7 +334,7 @@ func commitAssignment(booking *models.PickupBooking, candidate *milerCandidate,
|
||||
Source: "internal/assignment.commitAssignment",
|
||||
}); err != nil {
|
||||
tx.Rollback()
|
||||
return fmt.Errorf("record assigned stage: %w", err)
|
||||
return nil, fmt.Errorf("record assigned stage: %w", err)
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
@@ -323,7 +359,7 @@ func commitAssignment(booking *models.PickupBooking, candidate *milerCandidate,
|
||||
// best-effort and off this goroutine's critical path.
|
||||
routing.SequenceMilerStopsAsync(milerUserID)
|
||||
|
||||
return nil
|
||||
return candidate, nil
|
||||
}
|
||||
|
||||
// publishAssignment sends the booking.assigned event to NATS JetStream.
|
||||
|
||||
Reference in New Issue
Block a user