internal modifications
This commit is contained in:
@@ -65,6 +65,11 @@ func AssignCRMMiler(bookingID int) {
|
||||
"booking_id", bookingID,
|
||||
"max_retries", maxRetries,
|
||||
)
|
||||
|
||||
// Terminal failure — same handoff as the B2C path. Both entry points must
|
||||
// publish, or failures arriving via the CRM console stay invisible to the
|
||||
// DispatchAgent.
|
||||
publishAssignmentFailed(bookingID, reasonNoMilerAvailable)
|
||||
}
|
||||
|
||||
// tryAssign performs a single attempt: queries Redis GEO, scores candidates, commits.
|
||||
|
||||
@@ -58,6 +58,11 @@ func AssignCustomerMiler(bookingID int) {
|
||||
"booking_id", bookingID,
|
||||
"max_retries", maxRetries,
|
||||
)
|
||||
|
||||
// Terminal failure — hand off to the AI layer's DispatchAgent, which owns
|
||||
// what happens next (coverage sweep, escalation). Reached only after every
|
||||
// retry is exhausted, so it fires at most once per booking.
|
||||
publishAssignmentFailed(bookingID, reasonNoMilerAvailable)
|
||||
}
|
||||
|
||||
// tryCustomerAssign performs one full attempt: GEO miler search → provider selection → commit.
|
||||
|
||||
79
internal/assignment/failure_event.go
Normal file
79
internal/assignment/failure_event.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package assignment
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"doormile/db"
|
||||
"doormile/models"
|
||||
"doormile/utils"
|
||||
)
|
||||
|
||||
// assignmentFailedSubject is the JetStream subject the AI layer's DispatchAgent
|
||||
// binds its durable consumer to. It must be present on the ASSIGNMENTS stream
|
||||
// before anything publishes here — JetStream drops messages on subjects the
|
||||
// stream does not cover, silently and with a successful-looking publish.
|
||||
const assignmentFailedSubject = "booking.assignment_failed"
|
||||
|
||||
// Reasons carried on the failure event. Kept coarse on purpose: the consumer
|
||||
// branches on booking_id and coordinates, and treats reason as diagnostic only.
|
||||
const (
|
||||
reasonNoMilerAvailable = "NO_MILER_AVAILABLE"
|
||||
)
|
||||
|
||||
// publishAssignmentFailed emits booking.assignment_failed after auto-assignment
|
||||
// has genuinely given up on a booking.
|
||||
//
|
||||
// Call this only from the terminal branch of an assignment entry point — after
|
||||
// every retry is exhausted — never from inside a single attempt. The per-attempt
|
||||
// paths (no eligible candidates, decision-engine escalation) fire up to
|
||||
// maxRetries times for a booking that may still succeed on a later attempt, and
|
||||
// publishing there would report failures that never happened.
|
||||
//
|
||||
// Mirrors publishAssignment/publishCustomerAssignment: non-fatal, so a NATS
|
||||
// outage degrades to a log line rather than breaking the assignment goroutine.
|
||||
func publishAssignmentFailed(bookingID int, reason string) {
|
||||
if db.Js == nil {
|
||||
return
|
||||
}
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"booking_id": bookingID,
|
||||
"reason": reason,
|
||||
"failed_at": time.Now().UnixMilli(),
|
||||
}
|
||||
|
||||
// Pickup coordinates drive the consumer's coverage sweep. They aren't in
|
||||
// scope at the call site, so re-read the booking — one indexed lookup on a
|
||||
// path that only runs after retries have already been exhausted. If the
|
||||
// read fails we still publish: the consumer treats missing coordinates as
|
||||
// "skip the sweep" and degrades safely rather than losing the event.
|
||||
var booking models.PickupBooking
|
||||
if err := db.DB.Select("pickuplatitude", "pickuplongitude", "bookingno").
|
||||
First(&booking, bookingID).Error; err != nil {
|
||||
utils.Warn("Assignment: could not load booking for failure event; publishing without coordinates",
|
||||
"booking_id", bookingID, "error", err)
|
||||
} else {
|
||||
payload["booking_no"] = booking.Bookingno
|
||||
if booking.Pickuplatitude != 0 || booking.Pickuplongitude != 0 {
|
||||
payload["lat"] = booking.Pickuplatitude
|
||||
payload["lon"] = booking.Pickuplongitude
|
||||
}
|
||||
}
|
||||
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
utils.Warn("Assignment: failed to marshal assignment-failed payload",
|
||||
"booking_id", bookingID, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := db.Js.Publish(assignmentFailedSubject, data); err != nil {
|
||||
utils.Warn("Assignment: NATS publish failed for assignment-failed event",
|
||||
"booking_id", bookingID, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
utils.Info("Assignment: published "+assignmentFailedSubject,
|
||||
"booking_id", bookingID, "reason", reason)
|
||||
}
|
||||
Reference in New Issue
Block a user