Add assignment engine, FCM, WebSockets, city gate, and internal APIs
- internal/assignment: GEORADIUS miler assignment with retry/escalation, customer-side provider scoring, FCM notifications on assign - internal/notify: Firebase Admin SDK (FCM) client initialisation - internal/ws: WebSocket handlers for live parcel tracking and customer↔miler chat - middlewares: city gate (pincode prefix validation), internal API key auth, WebSocket JWT auth - controllers: InternalNotify + InternalReassign for machine-to-machine calls; pricing helpers wired into CreateCustomerBooking and CreateCRMBooking - routes: /internal/*, /ws/bookings/:id/track, /ws/bookings/:id/chat - models/users, models/doormile_pricing: new fields for device tokens, assignment state, pricing bands - seed_data.sql: initial pricing seed rows .env and Firebase service-account JSON intentionally excluded. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
"doormile/constants"
|
||||
"doormile/db"
|
||||
"doormile/dto"
|
||||
"doormile/internal/assignment"
|
||||
"doormile/models"
|
||||
"doormile/utils"
|
||||
|
||||
@@ -454,34 +455,23 @@ func CreateCustomerBooking(c *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
var distance float64
|
||||
if booking.Deliverylatitude != 0 && booking.Deliverylongitude != 0 {
|
||||
distance = calculateDistance(booking.Pickuplatitude, booking.Pickuplongitude, booking.Deliverylatitude, booking.Deliverylongitude)
|
||||
}
|
||||
|
||||
var pricing models.Pricing
|
||||
pricingErr := tx.Where("status = ? AND ? BETWEEN effectivefrom AND effectiveto", "Active", time.Now()).Order("priority DESC").First(&pricing).Error
|
||||
|
||||
var estimatedPrice float64
|
||||
var pricingID *int
|
||||
if pricingErr == nil {
|
||||
pricingID = &pricing.Pricingid
|
||||
kmExtra := math.Max(0, distance-pricing.Basedistance)
|
||||
kgExtra := math.Max(0, totalWeight-pricing.Baseweight)
|
||||
estimatedPrice = pricing.Baseprice + (kmExtra * pricing.Priceperkm) + (kgExtra * pricing.Priceperkg) + pricing.Handlingcharges
|
||||
} else {
|
||||
estimatedPrice = 50.0 + (distance * 5.0) + (totalWeight * 10.0)
|
||||
}
|
||||
|
||||
serviceType := req.ServiceOption
|
||||
if serviceType == "" {
|
||||
serviceType = "Normal"
|
||||
}
|
||||
|
||||
if serviceType == "Fast" {
|
||||
estimatedPrice *= 1.25
|
||||
} else if serviceType == "Superfast" {
|
||||
estimatedPrice *= 1.5
|
||||
zone := resolveZone(req.Pickuppincode, req.Deliverypincode)
|
||||
itemCategory := normalizePricingCategory(req.Parcels[0].Itemcategory)
|
||||
|
||||
var estimatedPrice float64
|
||||
if price, found := lookupDoormilePrice(zone, mapServiceTypeToPricing(serviceType), totalWeight, itemCategory); found {
|
||||
estimatedPrice = price
|
||||
} else {
|
||||
var distance float64
|
||||
if booking.Deliverylatitude != 0 && booking.Deliverylongitude != 0 {
|
||||
distance = calculateDistance(booking.Pickuplatitude, booking.Pickuplongitude, booking.Deliverylatitude, booking.Deliverylongitude)
|
||||
}
|
||||
estimatedPrice = 50.0 + (distance * 5.0) + (totalWeight * 10.0)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
@@ -501,7 +491,6 @@ func CreateCustomerBooking(c *fiber.Ctx) error {
|
||||
Estimatedprice: estimatedPrice,
|
||||
Estimateddeliveryat: &estDelivery,
|
||||
Sladueat: &slaDue,
|
||||
Pricingid: pricingID,
|
||||
}
|
||||
|
||||
if err := tx.Create(&srvOption).Error; err != nil {
|
||||
@@ -521,6 +510,8 @@ func CreateCustomerBooking(c *fiber.Ctx) error {
|
||||
|
||||
tx.Commit()
|
||||
|
||||
go assignment.AssignCustomerMiler(booking.Bookingid)
|
||||
|
||||
if db.Js != nil {
|
||||
payload := map[string]interface{}{
|
||||
"booking_id": booking.Bookingid,
|
||||
@@ -642,3 +633,25 @@ func TrackConsignment(c *fiber.Ctx) error {
|
||||
"history": history,
|
||||
})
|
||||
}
|
||||
|
||||
func SaveCustomerDeviceToken(c *fiber.Ctx) error {
|
||||
customerID := c.Locals("userid").(int)
|
||||
|
||||
var req struct {
|
||||
DeviceToken string `json:"device_token"`
|
||||
}
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return utils.BadRequest(c, "invalid request body")
|
||||
}
|
||||
if req.DeviceToken == "" {
|
||||
return utils.BadRequest(c, "device_token is required")
|
||||
}
|
||||
|
||||
if err := db.DB.Model(&models.AppCustomer{}).
|
||||
Where("appcustomerid = ?", customerID).
|
||||
Update("device_token", req.DeviceToken).Error; err != nil {
|
||||
return utils.Internal(c, "failed to save device token")
|
||||
}
|
||||
|
||||
return utils.Message(c, "device token saved")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user