Files
doormile_backend/controllers/milerController.go
2026-06-22 17:43:40 +05:30

884 lines
24 KiB
Go

package controllers
import (
"context"
"crypto/rand"
"encoding/json"
"fmt"
"math"
"strconv"
"time"
"doormile/config"
"doormile/constants"
"doormile/db"
"doormile/dto"
"doormile/models"
"doormile/utils"
"github.com/gofiber/fiber/v2"
"github.com/redis/go-redis/v9"
)
func generateTrackingNo() string {
b := make([]byte, 4)
rand.Read(b)
return fmt.Sprintf("DM-TRK-%X-%d", b, time.Now().Unix()%100000)
}
func LoginMiler(cfg *config.Config) fiber.Handler {
return func(c *fiber.Ctx) error {
req := new(dto.MilerLoginRequest)
if err := c.BodyParser(req); err != nil {
return utils.BadRequest(c, "invalid request body")
}
if req.Phone == "" {
return utils.BadRequest(c, "phone is required")
}
configID := req.Configid
if configID == 0 {
configID = 1001
}
var user models.AppUser
if err := db.DB.Where("contactno = ? AND configid = ?", req.Phone, configID).First(&user).Error; err != nil {
return utils.NotFound(c, "no miler account found for this phone number")
}
if user.Roleid != 5 {
return utils.Forbidden(c, "this endpoint is restricted to miler accounts")
}
if user.Status != "Active" {
return utils.Forbidden(c, "miler account is not active")
}
return c.JSON(fiber.Map{
"success": true,
"message": "PIN verification required",
"phone": req.Phone,
})
}
}
func VerifyMilerPin(cfg *config.Config) fiber.Handler {
return func(c *fiber.Ctx) error {
req := new(dto.MilerPinVerifyRequest)
if err := c.BodyParser(req); err != nil {
return utils.BadRequest(c, "invalid request body")
}
if req.Phone == "" || req.Pin == "" {
return utils.BadRequest(c, "phone and pin are required")
}
configID := req.Configid
if configID == 0 {
configID = 1001
}
var user models.AppUser
if err := db.DB.Where("contactno = ? AND configid = ?", req.Phone, configID).First(&user).Error; err != nil {
return utils.NotFound(c, "no miler account found for this phone number")
}
if user.Roleid != 5 {
return utils.Forbidden(c, "this endpoint is restricted to miler accounts")
}
if user.Status != "Active" {
return utils.Forbidden(c, "miler account is not active")
}
if !utils.CheckPasswordHash(req.Pin, user.Password) {
return utils.Unauthorized(c, "incorrect PIN")
}
token, err := utils.GenerateToken(user.Userid, user.Email, user.Roleid, user.Tenantid, user.Configid, cfg.JWTSecret)
if err != nil {
return utils.Internal(c, "failed to generate token")
}
var profile models.MilerProfile
if err := db.DB.Where("userid = ?", user.Userid).First(&profile).Error; err != nil {
profile = models.MilerProfile{
Userid: user.Userid,
Displayname: user.Authname,
Phone: user.Contactno,
Availabilitystatus: constants.MilerOffline,
Rating: 5.0,
Applocationid: user.Applocationid,
}
db.DB.Create(&profile)
}
return c.JSON(fiber.Map{
"success": true,
"token": token,
"user": fiber.Map{
"userid": user.Userid,
"authname": user.Authname,
"email": user.Email,
"contactno": user.Contactno,
"profile": profile,
},
})
}
}
func GetMilerProfile(c *fiber.Ctx) error {
milerUserID := c.Locals("userid").(int)
var user models.AppUser
if err := db.DB.First(&user, milerUserID).Error; err != nil {
return utils.NotFound(c, "user not found")
}
var profile models.MilerProfile
if err := db.DB.Where("userid = ?", milerUserID).First(&profile).Error; err != nil {
return utils.NotFound(c, "miler profile not found")
}
return utils.OK(c, fiber.Map{
"userid": user.Userid,
"authname": user.Authname,
"email": user.Email,
"contactno": user.Contactno,
"profile": profile,
})
}
func UpdateMilerProfile(c *fiber.Ctx) error {
milerUserID := c.Locals("userid").(int)
var profile models.MilerProfile
if err := db.DB.Where("userid = ?", milerUserID).First(&profile).Error; err != nil {
return utils.NotFound(c, "miler profile not found")
}
type ProfileUpdate struct {
Displayname string `json:"displayname"`
Profilephotourl string `json:"profilephotourl"`
Defaultvehicletype string `json:"defaultvehicletype"`
Phone string `json:"phone"`
}
req := new(ProfileUpdate)
if err := c.BodyParser(req); err != nil {
return utils.BadRequest(c, "invalid request body")
}
if req.Displayname != "" {
profile.Displayname = req.Displayname
}
if req.Phone != "" {
profile.Phone = req.Phone
}
profile.Profilephotourl = req.Profilephotourl
profile.Defaultvehicletype = req.Defaultvehicletype
profile.Updatedat = time.Now()
if err := db.DB.Save(&profile).Error; err != nil {
return utils.Internal(c, "failed to update profile")
}
return utils.OK(c, profile)
}
func UpdateMilerLocation(c *fiber.Ctx) error {
milerUserID := c.Locals("userid").(int)
req := new(dto.MilerLocationUpdateRequest)
if err := c.BodyParser(req); err != nil {
return utils.BadRequest(c, "invalid request body")
}
if req.Latitude == 0 || req.Longitude == 0 {
return utils.BadRequest(c, "latitude and longitude are required")
}
var profile models.MilerProfile
if err := db.DB.Where("userid = ?", milerUserID).First(&profile).Error; err != nil {
return utils.NotFound(c, "miler profile not found")
}
profile.Currentlatitude = req.Latitude
profile.Currentlongitude = req.Longitude
profile.Currentpincode = req.Pincode
now := time.Now()
profile.Lastlocationupdatedat = &now
profile.Updatedat = now
if err := db.DB.Save(&profile).Error; err != nil {
return utils.Internal(c, "failed to update location")
}
if db.Rdb != nil {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
redisKey := fmt.Sprintf("miler:gps:%d", milerUserID)
val := fmt.Sprintf("%f,%f", req.Latitude, req.Longitude)
db.Rdb.Set(ctx, redisKey, val, 30*time.Minute)
db.Rdb.GeoAdd(ctx, "milers:locations", &redis.GeoLocation{
Name: strconv.Itoa(milerUserID),
Latitude: req.Latitude,
Longitude: req.Longitude,
})
}
return utils.OK(c, fiber.Map{
"latitude": req.Latitude,
"longitude": req.Longitude,
"pincode": req.Pincode,
})
}
func UpdateMilerAvailability(c *fiber.Ctx) error {
milerUserID := c.Locals("userid").(int)
req := new(dto.MilerAvailabilityRequest)
if err := c.BodyParser(req); err != nil {
return utils.BadRequest(c, "invalid request body")
}
if req.Status == "" {
return utils.BadRequest(c, "status is required")
}
var profile models.MilerProfile
if err := db.DB.Where("userid = ?", milerUserID).First(&profile).Error; err != nil {
return utils.NotFound(c, "miler profile not found")
}
profile.Availabilitystatus = req.Status
profile.Updatedat = time.Now()
if err := db.DB.Save(&profile).Error; err != nil {
return utils.Internal(c, "failed to update availability")
}
var user models.AppUser
if err := db.DB.First(&user, milerUserID).Error; err == nil {
if req.Status == constants.MilerOffline || req.Status == constants.MilerBlocked {
user.Onduty = 0
} else {
user.Onduty = 1
}
db.DB.Save(&user)
}
return utils.OK(c, profile)
}
func GetMilerAssignments(c *fiber.Ctx) error {
milerUserID := c.Locals("userid").(int)
var assignments []models.BookingAssignment
if err := db.DB.Where("mileruserid = ?", milerUserID).Order("assignedat DESC").Find(&assignments).Error; err != nil {
return utils.Internal(c, "failed to fetch assignments")
}
return utils.List(c, assignments, int64(len(assignments)))
}
func GetMilerAssignmentDetails(c *fiber.Ctx) error {
milerUserID := c.Locals("userid").(int)
assignmentID, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utils.BadRequest(c, "invalid assignment ID")
}
var assignment models.BookingAssignment
if err := db.DB.Where("bookingassignmentid = ? AND mileruserid = ?", assignmentID, milerUserID).First(&assignment).Error; err != nil {
return utils.NotFound(c, "assignment not found")
}
var booking models.PickupBooking
db.DB.Preload("Parcels").Preload("ServiceOptions").First(&booking, assignment.Bookingid)
return utils.OK(c, fiber.Map{
"assignment": assignment,
"booking": booking,
})
}
func AcceptMilerAssignment(c *fiber.Ctx) error {
milerUserID := c.Locals("userid").(int)
assignmentID, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utils.BadRequest(c, "invalid assignment ID")
}
tx := db.DB.Begin()
var assignment models.BookingAssignment
if err := tx.Where("bookingassignmentid = ? AND mileruserid = ?", assignmentID, milerUserID).First(&assignment).Error; err != nil {
tx.Rollback()
return utils.NotFound(c, "assignment not found")
}
now := time.Now()
assignment.Assignmentstatus = constants.AssignmentAccepted
assignment.Acceptedat = &now
tx.Save(&assignment)
var booking models.PickupBooking
if err := tx.First(&booking, assignment.Bookingid).Error; err == nil {
booking.Status = constants.BookingPickupScheduled
booking.Assignedmileruserid = &milerUserID
booking.Updatedat = now
tx.Save(&booking)
}
tx.Model(&models.MilerProfile{}).Where("userid = ?", milerUserID).Update("availabilitystatus", constants.MilerAssigned)
tx.Commit()
return utils.Message(c, "assignment accepted successfully")
}
func RejectMilerAssignment(c *fiber.Ctx) error {
milerUserID := c.Locals("userid").(int)
assignmentID, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utils.BadRequest(c, "invalid assignment ID")
}
tx := db.DB.Begin()
var assignment models.BookingAssignment
if err := tx.Where("bookingassignmentid = ? AND mileruserid = ?", assignmentID, milerUserID).First(&assignment).Error; err != nil {
tx.Rollback()
return utils.NotFound(c, "assignment not found")
}
assignment.Assignmentstatus = constants.AssignmentRejected
assignment.Remarks = c.Query("reason", "Rejected by rider")
tx.Save(&assignment)
var booking models.PickupBooking
if err := tx.First(&booking, assignment.Bookingid).Error; err == nil {
booking.Status = constants.BookingCreated
booking.Assignedmileruserid = nil
booking.Updatedat = time.Now()
tx.Save(&booking)
}
tx.Model(&models.MilerProfile{}).Where("userid = ?", milerUserID).Update("availabilitystatus", constants.MilerAvailable)
tx.Commit()
return utils.Message(c, "assignment rejected")
}
func BookingReachedCustomer(c *fiber.Ctx) error {
milerUserID := c.Locals("userid").(int)
bookingID, err := strconv.Atoi(c.Params("bookingid"))
if err != nil {
return utils.BadRequest(c, "invalid booking ID")
}
tx := db.DB.Begin()
var booking models.PickupBooking
if err := tx.Where("bookingid = ? AND assignedmileruserid = ?", bookingID, milerUserID).First(&booking).Error; err != nil {
tx.Rollback()
return utils.NotFound(c, "assigned booking not found")
}
tx.Model(&models.MilerProfile{}).Where("userid = ?", milerUserID).Update("availabilitystatus", constants.MilerAtCustomer)
tx.Commit()
return utils.Message(c, "arrival at customer confirmed")
}
func BookingParcelConfirm(c *fiber.Ctx) error {
milerUserID := c.Locals("userid").(int)
bookingID, err := strconv.Atoi(c.Params("bookingid"))
if err != nil {
return utils.BadRequest(c, "invalid booking ID")
}
var booking models.PickupBooking
if err := db.DB.Where("bookingid = ? AND assignedmileruserid = ?", bookingID, milerUserID).First(&booking).Error; err != nil {
return utils.NotFound(c, "assigned booking not found")
}
type ParcelUpdate struct {
Weight float64 `json:"weight"`
Length float64 `json:"length"`
Width float64 `json:"width"`
Height float64 `json:"height"`
}
req := new(ParcelUpdate)
if err := c.BodyParser(req); err != nil {
return utils.BadRequest(c, "invalid request body")
}
var parcel models.BookingParcel
if err := db.DB.Where("bookingid = ?", bookingID).First(&parcel).Error; err != nil {
return utils.NotFound(c, "parcel details not found")
}
parcel.Weight = req.Weight
parcel.Length = req.Length
parcel.Width = req.Width
parcel.Height = req.Height
parcel.Updatedat = time.Now()
db.DB.Save(&parcel)
return utils.OK(c, parcel)
}
func BookingPaymentCollect(c *fiber.Ctx) error {
milerUserID := c.Locals("userid").(int)
bookingID, err := strconv.Atoi(c.Params("bookingid"))
if err != nil {
return utils.BadRequest(c, "invalid booking ID")
}
var booking models.PickupBooking
if err := db.DB.Where("bookingid = ? AND assignedmileruserid = ?", bookingID, milerUserID).First(&booking).Error; err != nil {
return utils.NotFound(c, "assigned booking not found")
}
req := new(dto.PaymentRequest)
if err := c.BodyParser(req); err != nil {
return utils.BadRequest(c, "invalid request body")
}
if req.Amount <= 0 {
return utils.BadRequest(c, "payment amount must be greater than zero")
}
now := time.Now()
payment := models.BookingPayment{
Bookingid: bookingID,
Amount: req.Amount,
Paymentmode: req.Paymentmode,
Paymentstatus: constants.PaymentStatusPaid,
Collectedbyuserid: &milerUserID,
Transactionref: req.Transactionref,
Paidat: &now,
}
if err := db.DB.Create(&payment).Error; err != nil {
return utils.Internal(c, "failed to record payment")
}
return utils.Created(c, payment)
}
func BookingPickupComplete(c *fiber.Ctx) error {
milerUserID := c.Locals("userid").(int)
bookingID, err := strconv.Atoi(c.Params("bookingid"))
if err != nil {
return utils.BadRequest(c, "invalid booking ID")
}
tx := db.DB.Begin()
var booking models.PickupBooking
if err := tx.Where("bookingid = ? AND assignedmileruserid = ?", bookingID, milerUserID).First(&booking).Error; err != nil {
tx.Rollback()
return utils.NotFound(c, "assigned booking not found")
}
now := time.Now()
booking.Status = constants.BookingPickedUp
booking.Updatedat = now
tx.Save(&booking)
var profile models.MilerProfile
if err := tx.Where("userid = ?", milerUserID).First(&profile).Error; err == nil {
profile.Totalcompletedpickups += 1
profile.Availabilitystatus = constants.MilerPickedUp
profile.Updatedat = now
tx.Save(&profile)
}
var parcel models.BookingParcel
tx.Where("bookingid = ?", bookingID).First(&parcel)
volumetric := calculateVolumetricWeight(parcel.Length, parcel.Width, parcel.Height)
chargeable := math.Max(parcel.Weight, volumetric)
trackingNo := generateTrackingNo()
var defaultHubID *int
var hub models.Hub
if tx.First(&hub).Error == nil {
defaultHubID = &hub.Hubid
}
consignment := models.Consignment{
Trackingno: trackingNo,
Tenantid: c.Locals("tenantid").(int),
Pickuplatitude: booking.Pickuplatitude,
Pickuplongitude: booking.Pickuplongitude,
Deliverylatitude: booking.Deliverylatitude,
Deliverylongitude: booking.Deliverylongitude,
Pickuppincode: booking.Pickuppincode,
Deliverypincode: booking.Deliverypincode,
Length: parcel.Length,
Width: parcel.Width,
Height: parcel.Height,
Deadweight: parcel.Weight,
Volumetricweight: volumetric,
Chargeableweight: chargeable,
Paymentmode: "Prepaid",
Status: constants.ConsignmentInwardedAtHub,
Estimateddeliveryat: nil,
Createdby: milerUserID,
Originhubid: defaultHubID,
Currenthubid: defaultHubID,
}
var payment models.BookingPayment
if tx.Where("bookingid = ?", bookingID).First(&payment).Error == nil {
if payment.Paymentstatus == constants.PaymentStatusPaid {
consignment.Codcollected = payment.Amount
} else {
consignment.Codamount = payment.Amount
consignment.Paymentmode = "COD"
}
}
if err := tx.Create(&consignment).Error; err != nil {
tx.Rollback()
return utils.Internal(c, "failed to convert booking to consignment")
}
booking.Consignmentid = &consignment.Consignmentid
booking.Status = constants.BookingConvertedConsignment
tx.Save(&booking)
history := models.ConsignmentHistory{
Consignmentid: consignment.Consignmentid,
Hubid: defaultHubID,
Userid: &milerUserID,
Eventstatus: constants.ConsignmentInwardedAtHub,
Remarks: "Package collected by miler and converted to consignment",
}
tx.Create(&history)
tx.Model(&models.MilerProfile{}).Where("userid = ?", milerUserID).Update("availabilitystatus", constants.MilerAvailable)
tx.Commit()
return utils.OK(c, fiber.Map{
"tracking_no": trackingNo,
"consignment_id": consignment.Consignmentid,
"booking_no": booking.Bookingno,
})
}
func BookingVehicleRequiredEscalate(c *fiber.Ctx) error {
milerUserID := c.Locals("userid").(int)
bookingID, err := strconv.Atoi(c.Params("bookingid"))
if err != nil {
return utils.BadRequest(c, "invalid booking ID")
}
var booking models.PickupBooking
if err := db.DB.Where("bookingid = ? AND assignedmileruserid = ?", bookingID, milerUserID).First(&booking).Error; err != nil {
return utils.NotFound(c, "assigned booking not found")
}
reqVeh := models.BookingVehicleRequirement{
Bookingid: bookingID,
Requiredvehicletype: c.Query("type", "truck"),
Reason: c.Query("reason", "Package is too large for bike rider"),
Status: "Required",
}
if err := db.DB.Create(&reqVeh).Error; err != nil {
return utils.Internal(c, "failed to register vehicle requirement")
}
return utils.Created(c, reqVeh)
}
func CreateMilerPeriodicLog(c *fiber.Ctx) error {
ctx := context.Background()
var log models.MilerLog
if err := c.BodyParser(&log); err != nil {
return utils.BadRequest(c, "invalid request body")
}
t, err := time.Parse("2006-01-02 15:04:05", log.LogDate)
if err != nil {
return utils.BadRequest(c, "invalid logdate format — expected YYYY-MM-DD HH:MM:SS")
}
timestamp := t.Unix()
logKey := fmt.Sprintf("miler_periodic_log:%d:%d", log.UserID, timestamp)
data, _ := json.Marshal(log)
if db.Rdb != nil {
if err := db.Rdb.Set(ctx, logKey, data, 0).Err(); err != nil {
return utils.Internal(c, "failed to store log")
}
userZsetKey := fmt.Sprintf("miler_periodic_logs:%d", log.UserID)
db.Rdb.ZAdd(ctx, userZsetKey, redis.Z{Score: float64(timestamp), Member: logKey})
db.Rdb.ZAdd(ctx, "miler_periodic_logs_all", redis.Z{Score: float64(timestamp), Member: logKey})
}
return utils.Message(c, "miler periodic log stored successfully")
}
func GetMilerPeriodicLogs(c *fiber.Ctx) error {
ctx := context.Background()
if db.Rdb == nil {
return utils.Internal(c, "cache service unavailable")
}
userID := c.Query("userid")
var keys []string
var err error
if userID != "" {
zsetKey := fmt.Sprintf("miler_periodic_logs:%s", userID)
keys, err = db.Rdb.ZRevRange(ctx, zsetKey, 0, 0).Result()
} else {
keys, err = db.Rdb.ZRevRange(ctx, "miler_periodic_logs_all", 0, 0).Result()
}
if err != nil {
return utils.Internal(c, "failed to fetch logs")
}
if len(keys) == 0 {
return utils.List(c, []interface{}{}, 0)
}
val, err := db.Rdb.Get(ctx, keys[0]).Result()
if err != nil {
return utils.Internal(c, "failed to retrieve log data")
}
var log map[string]interface{}
json.Unmarshal([]byte(val), &log)
return utils.OK(c, log)
}
func CreateMilerStatus(c *fiber.Ctx) error {
ctx := context.Background()
var status models.MilerStatus
if err := c.BodyParser(&status); err != nil {
return utils.BadRequest(c, "invalid request body")
}
if status.UserID == 0 || status.Status == "" {
return utils.BadRequest(c, "userid and status are required")
}
key := fmt.Sprintf("miler_status:%d", status.UserID)
data, _ := json.Marshal(status)
if db.Rdb != nil {
if err := db.Rdb.Set(ctx, key, data, 0).Err(); err != nil {
return utils.Internal(c, "failed to store status")
}
db.Rdb.ZAdd(ctx, "miler_status_all", redis.Z{
Score: float64(time.Now().Unix()),
Member: key,
})
}
return utils.Message(c, "miler status updated successfully")
}
func GetMilerStatus(c *fiber.Ctx) error {
ctx := context.Background()
if db.Rdb == nil {
return utils.Internal(c, "cache service unavailable")
}
userIDStr := c.Query("userid")
if userIDStr != "" {
key := fmt.Sprintf("miler_status:%s", userIDStr)
val, err := db.Rdb.Get(ctx, key).Result()
if err != nil {
return utils.NotFound(c, "status not found for this miler")
}
var data map[string]interface{}
json.Unmarshal([]byte(val), &data)
return utils.OK(c, data)
}
pageStr := c.Query("page")
pageSizeStr := c.Query("pagesize")
page, _ := strconv.Atoi(pageStr)
pageSize, _ := strconv.Atoi(pageSizeStr)
var start, end int64
if page > 0 && pageSize > 0 {
offset := (page - 1) * pageSize
start = int64(offset)
end = int64(offset + pageSize - 1)
} else {
start = 0
end = -1
}
keys, err := db.Rdb.ZRevRange(ctx, "miler_status_all", start, end).Result()
if err != nil {
return utils.Internal(c, "failed to fetch statuses")
}
if len(keys) == 0 {
return utils.List(c, []interface{}{}, 0)
}
values, err := db.Rdb.MGet(ctx, keys...).Result()
if err != nil {
return utils.Internal(c, "failed to retrieve status data")
}
var result []map[string]interface{}
for _, val := range values {
if val == nil {
continue
}
var item map[string]interface{}
json.Unmarshal([]byte(val.(string)), &item)
result = append(result, item)
}
return utils.List(c, result, int64(len(result)))
}
func PublishConsignmentLogs(c *fiber.Ctx) error {
var input []models.ConsignmentLog
if err := c.BodyParser(&input); err != nil {
return utils.BadRequest(c, "invalid request body")
}
if len(input) == 0 {
return utils.BadRequest(c, "at least one log entry is required")
}
if db.Rdb == nil {
return utils.Internal(c, "cache service unavailable")
}
pipe := db.Rdb.TxPipeline()
tx := db.DB.Begin()
for _, item := range input {
logTime, err := time.Parse("2006-01-02 15:04:05", item.LogDate)
if err != nil {
logTime = time.Now()
}
ts := logTime.Unix()
logKey := "Consignmentlogs:" + strconv.Itoa(item.ConsignmentID)
userIndexKey := "user:consignmentlogs:" + strconv.Itoa(item.UserID)
jsonData, _ := json.Marshal(item)
pipe.RPush(db.Ctx, logKey, jsonData)
pipe.ZAdd(db.Ctx, userIndexKey, redis.Z{
Score: float64(ts),
Member: item.ConsignmentID,
})
history := models.ConsignmentHistory{
Consignmentid: item.ConsignmentID,
Userid: &item.UserID,
Eventstatus: item.Status,
Remarks: fmt.Sprintf("GPS Update: Lat %s, Lon %s. Speed %s. Remarks: %s", item.Latitude, item.Longitude, item.Speed, item.Remarks),
Createdat: logTime,
}
if err := tx.Create(&history).Error; err != nil {
tx.Rollback()
return utils.Internal(c, "failed to persist consignment log")
}
}
if _, err := pipe.Exec(db.Ctx); err != nil {
tx.Rollback()
return utils.Internal(c, "failed to publish logs to cache")
}
tx.Commit()
return utils.Message(c, "consignment logs published successfully")
}
func GetConsignmentLogs(c *fiber.Ctx) error {
consignmentID, err := strconv.Atoi(c.Params("consignmentid"))
if err != nil {
return utils.BadRequest(c, "invalid consignment ID")
}
if db.Rdb == nil {
return utils.Internal(c, "cache service unavailable")
}
logKey := "Consignmentlogs:" + strconv.Itoa(consignmentID)
redisList, err := db.Rdb.LRange(db.Ctx, logKey, 0, -1).Result()
if err == nil && len(redisList) > 0 {
var logs []map[string]interface{}
for _, raw := range redisList {
var m map[string]interface{}
json.Unmarshal([]byte(raw), &m)
logs = append(logs, m)
}
return utils.List(c, logs, int64(len(logs)))
}
var history []models.ConsignmentHistory
if err := db.DB.Where("consignmentid = ?", consignmentID).Order("createdat ASC").Find(&history).Error; err != nil {
return utils.Internal(c, "failed to fetch consignment logs")
}
return utils.List(c, history, int64(len(history)))
}
func GetUserConsignmentLogs(c *fiber.Ctx) error {
userID, err := strconv.Atoi(c.Params("userid"))
if err != nil {
return utils.BadRequest(c, "invalid user ID")
}
if db.Rdb == nil {
return utils.Internal(c, "cache service unavailable")
}
userIndexKey := "user:consignmentlogs:" + strconv.Itoa(userID)
members, err := db.Rdb.ZRevRange(db.Ctx, userIndexKey, 0, -1).Result()
if err != nil {
return utils.Internal(c, "failed to fetch consignment log index")
}
var logs []map[string]interface{}
for _, consignmentIDStr := range members {
logKey := "Consignmentlogs:" + consignmentIDStr
rawList, err := db.Rdb.LRange(db.Ctx, logKey, -1, -1).Result()
if err == nil && len(rawList) > 0 {
var m map[string]interface{}
json.Unmarshal([]byte(rawList[0]), &m)
logs = append(logs, m)
}
}
return utils.List(c, logs, int64(len(logs)))
}