milergeo added

This commit is contained in:
2026-10-06 12:26:57 +05:30
parent 220e934045
commit 9b94be1f07
10 changed files with 329 additions and 44 deletions

View File

@@ -149,12 +149,16 @@ func collectEligibleCandidates(nearby []redis.GeoLocation) ([]*milerCandidate, [
continue
}
// Only today's open stops count towards the cap. An order assigned
// yesterday or last month and never closed (rider app not updated,
// order abandoned) used to hold the rider at the cap forever, so the
// one rider actually working was skipped for every new order.
var activeCount int64
db.DB.Model(&models.BookingAssignment{}).
Where("mileruserid = ? AND assignmentstatus IN ?", milerUserID, []string{
Where("mileruserid = ? AND assignmentstatus IN ? AND assignedat >= ?", milerUserID, []string{
constants.AssignmentAssigned,
constants.AssignmentAccepted,
}).
}, startOfISTDay(time.Now())).
Count(&activeCount)
if activeCount >= maxActiveBookings() {
@@ -185,6 +189,14 @@ func collectEligibleCandidates(nearby []redis.GeoLocation) ([]*milerCandidate, [
return candidates, aiCandidates
}
// startOfISTDay is midnight today in India, the boundary for "today's"
// open stops. An absolute instant, so it compares correctly with assignedat
// whether the column stores IST wall-clock or a real timestamp.
func startOfISTDay(now time.Time) time.Time {
n := now.In(utils.ISTLocation())
return time.Date(n.Year(), n.Month(), n.Day(), 0, 0, 0, 0, utils.ISTLocation())
}
// ─── Per-miler stats ─────────────────────────────────────────────────────────
func fetchMilerStats(milerUserID int) (onTimeRate float64, completedToday int64) {

View File

@@ -12,6 +12,7 @@ import (
"doormile/constants"
"doormile/db"
"doormile/internal/cxstage"
"doormile/internal/milergeo"
"doormile/internal/routing"
"doormile/models"
"doormile/utils"
@@ -196,22 +197,9 @@ func queryNearbyMilers(lat, lon float64) ([]redis.GeoLocation, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
locs, err := db.Rdb.GeoSearchLocation(ctx, "milers:locations", &redis.GeoSearchLocationQuery{
GeoSearchQuery: redis.GeoSearchQuery{
Longitude: lon,
Latitude: lat,
Radius: geoRadiusKm,
RadiusUnit: "km",
Sort: "ASC",
Count: geoMaxCount,
},
WithDist: true,
}).Result()
if err != nil {
return nil, err
}
return locs, nil
// milergeo falls back to GEORADIUS on Redis older than 6.2, where
// GEOSEARCH does not exist and every order would find "no riders".
return milergeo.Search(ctx, db.Rdb, lat, lon, geoRadiusKm, geoMaxCount)
}
// commitAssignment writes the BookingAssignment row, updates the booking and the

View File

@@ -1,6 +1,9 @@
package assignment
import "testing"
import (
"testing"
"time"
)
// The per-miler concurrent-stop cap.
//
@@ -30,3 +33,22 @@ func TestMaxActiveBookings(t *testing.T) {
}
}
}
// The cap counts only stops assigned since midnight India time, so an order
// left open yesterday or last month no longer blocks a working rider.
func TestStartOfISTDay(t *testing.T) {
ist := time.FixedZone("IST", 5*3600+30*60)
cases := []struct{ now, want time.Time }{
// 11:43 IST on 6 Oct -> 00:00 IST 6 Oct
{time.Date(2026, 10, 6, 11, 43, 0, 0, ist), time.Date(2026, 10, 6, 0, 0, 0, 0, ist)},
// 20:00 UTC on 5 Oct is already 01:30 on 6 Oct in India
{time.Date(2026, 10, 5, 20, 0, 0, 0, time.UTC), time.Date(2026, 10, 6, 0, 0, 0, 0, ist)},
// 18:29 UTC on 5 Oct is still 23:59 on 5 Oct in India
{time.Date(2026, 10, 5, 18, 29, 0, 0, time.UTC), time.Date(2026, 10, 5, 0, 0, 0, 0, ist)},
}
for _, c := range cases {
if got := startOfISTDay(c.now); !got.Equal(c.want) {
t.Errorf("startOfISTDay(%v) = %v, want %v", c.now, got, c.want)
}
}
}