Files
backend_jupiter/domain/partner.go
Suriya 3da5876e4c fix: deliverytime timestamp error and riderlogs read timeouts
CreateOrder had no fallback for Deliverytime (unlike Orderdate),
so it defaulted to "" and Postgres rejected it as an invalid
timestamp on every order creation - reproduced and confirmed via a
direct manual INSERT. Same fallback pattern as Orderdate now applies.

Also carries the v2.7.58 fix that was live but never committed:
GetRiderLogsv1 and UpdateRiderLogv1 scope a 15s timeout to their
riderlogs full-list LRANGE (900K+ entries, routinely 5-6.5s), instead
of the default 3s client timeout that was causing getriderlogs 500s.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 11:24:48 +05:30

1395 lines
35 KiB
Go

package domain
import (
"context"
"encoding/json"
"errors"
"nearle/db"
"nearle/models"
"nearle/utils"
"reflect"
"strconv"
"strings"
"time"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
func GetActiveRiders(partnerid, aid, uid int, keyword string, pageno, pagesize int) []models.RiderInfo {
data := make([]models.RiderInfo, 0) // ✅ IMPORTANT
var qb strings.Builder
var params []interface{}
offset := 0
if pageno > 0 && pagesize > 0 {
offset = (pageno - 1) * pagesize
}
qb.WriteString(`
WITH latest_logs AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY userid ORDER BY logdate DESC) AS rn
FROM riderlogs
WHERE logdate >= CURRENT_DATE
AND logdate < CURRENT_DATE + INTERVAL '1 day'
)
SELECT DISTINCT
a.userid,a.firstname,a.lastname,CONCAT(a.firstname,' ',a.lastname) AS fullname,a.contactno,a.userfcmtoken,a.partnerid,a.applocationid,c.identificationno,
c.registrationno,c.licenseno,c.vehiclename,c.vehicleno,d.shiftid,d.starttime,d.endtime,d.shifthours,d.basefare,d.fuelcharge,e.logdate,e.login,e.logout,
e.workhours,e.shorthours,e.logstatus,e.latitude,e.longitude,a.status,f.locationname AS applocation
FROM app_users a
JOIN ridersettings c ON c.userid = a.userid
JOIN ridershifts d ON d.shiftid = c.shiftid
JOIN latest_logs e ON e.userid = a.userid AND e.rn = 1
JOIN app_location f ON f.applocationid = a.applocationid
WHERE a.configid = 6
AND a.status = 'Active'
AND e.logstatus = 0
AND EXISTS (
SELECT 1
FROM app_userpools p
WHERE p.userid = a.userid
AND p.onduty = 1
)
`)
if aid != 0 {
qb.WriteString(" AND a.applocationid = ?")
params = append(params, aid)
}
if partnerid != 0 {
qb.WriteString(" AND a.partnerid = ?")
params = append(params, partnerid)
}
if uid != 0 {
qb.WriteString(" AND a.userid = ?")
params = append(params, uid)
}
if keyword != "" {
k := "%" + strings.ToLower(keyword) + "%"
qb.WriteString(`
AND (
LOWER(a.firstname) LIKE ? OR
LOWER(a.lastname) LIKE ? OR
LOWER(a.contactno) LIKE ? OR
LOWER(a.userid::text) LIKE ?
)
`)
for i := 0; i < 4; i++ {
params = append(params, k)
}
}
qb.WriteString(" ORDER BY a.userid DESC")
if pageno > 0 && pagesize > 0 {
qb.WriteString(" LIMIT ? OFFSET ?")
params = append(params, pagesize, offset)
}
utils.Logger.Debugf("ActiveRiders SQL: %s", qb.String())
utils.Logger.Debugf("Params: %v", params)
print(qb.String())
print(params)
db.DB.Raw(qb.String(), params...).Scan(&data)
return data
}
func GetActiveRidersv2(partnerid, aid, uid, tenantid int) []models.RiderInfo {
var data []models.RiderInfo
var q1 string
const riders = `SELECT DISTINCT a.*,a.userid, a.firstname, a.lastname,
CONCAT(a.firstname, ' ', a.lastname) AS fullname,
a.contactno, a.userfcmtoken, a.partnerid, a.applocationid,
c.identificationno, c.registrationno, c.licenseno, c.vehiclename, c.vehicleno,
d.shiftid, d.starttime, d.endtime, d.shifthours, d.basefare, d.fuelcharge,
e.logdate, e.login, e.logout, e.workhours, e.shorthours, e.logstatus, a.status,
f.locationname AS applocation
FROM
app_users a
LEFT JOIN ridersettings c ON a.userid = c.userid
INNER JOIN ridershifts d ON a.shiftid = d.shiftid
INNER JOIN (
SELECT r1.*
FROM riderlogs r1
INNER JOIN (
SELECT userid, MAX(logdate) AS max_logdate
FROM riderlogs
WHERE logdate::date = CURRENT_DATE
GROUP BY userid
) r2 ON r1.userid = r2.userid AND r1.logdate = r2.max_logdate
WHERE r1.logstatus = 0
) e ON a.userid = e.userid
INNER JOIN app_location f ON a.applocationid = f.applocationid
INNER JOIN app_locationconfig g ON g.applocationid = f.applocationid`
if aid != 0 {
q1 = riders + " WHERE a.configid = 6 AND a.status = 'Active' AND e.logdate::date = CURRENT_DATE AND e.logstatus = 0 AND a.applocationid = ?"
db.DB.Raw(q1, aid).Find(&data)
} else if partnerid != 0 {
q1 = riders + " WHERE a.configid = 6 AND a.status = 'Active' AND e.logdate::date = CURRENT_DATE AND e.logstatus = 0 AND a.partnerid = ?"
db.DB.Raw(q1, partnerid).Find(&data)
} else if tenantid != 0 {
q1 = riders + " WHERE a.status = 'Active' AND a.configid = 6 AND a.status = 'Active' AND a.tenantid = ?"
db.DB.Raw(q1, tenantid).Find(&data)
} else {
q1 = riders + " WHERE g.status = 'Active' AND a.configid = 6 AND a.status = 'Active' AND e.logdate::date = CURRENT_DATE AND e.logstatus = 0 AND g.userid = ?"
db.DB.Raw(q1, uid).Find(&data)
}
utils.Logger.Debugf("Query executed for riders")
return data
}
func GetRiderShifts(aid int) []models.Ridershifts {
var data []models.Ridershifts
q1 := "Select a.*,concat(a.starttime,'-',a.endtime) as shiftname from ridershifts a where a.applocationid=?"
db.DB.Raw(q1, aid).Find(&data)
return data
}
func GetRiderPricing(aid int) []models.RiderPricing {
var data []models.RiderPricing
q1 := `SELECT a.shiftid, a.basefare, a.additionalcharges, a.fuelcharge, a.applocationid, b.locationname from ridershifts a
LEFT JOIN app_location b ON a.applocationid = b.applocationid`
if aid > 0 {
q1 += ` WHERE a.applocationid=` + strconv.Itoa(aid)
}
db.DB.Raw(q1).Find(&data)
return data
}
func GetRiderPool(partnerid int) []models.RiderPool {
var data []models.RiderPool
q1 := `SELECT b.poolid,a.userid,a.firstname,a.lastname,CONCAT(a.firstname,' ',a.lastname) AS fullname,
a.contactno,a.userfcmtoken,a.partnerid,a.applocationid,
c.identificationno,c.registrationno,c.licenseno,c.vehiclename,c.vehicleno,
d.shiftid,d.starttime,d.endtime,d.shifthours,d.basefare,d.fuelcharge,a.status
FROM
app_users a
INNER JOIN app_userpools b ON a.userid=b.userid
INNER JOIN ridersettings c ON a.userid=c.userid
INNER JOIN ridershifts d ON c.shiftid=d.shiftid
WHERE a.configid=6 and a.status='Active' and a.partnerid=?`
db.DB.Raw(q1, partnerid).Find(&data)
return data
}
func GetRiderInfo(userid int) models.RiderInfo {
var data models.RiderInfo
q1 := `SELECT a.userid,a.firstname,a.lastname, CONCAT(a.firstname,' ',a.lastname) as fullname,
a.partnerid,a.configid,a.contactno,
a.address,a.suburb,a.city,a.state,a.postcode,a.latitude,a.longitude,
b.identificationno,b.vehicleno,b.vehiclename,b.licenseno,b.insuranceno,b.insurancedate,
c.shiftid,c.starttime,c.endtime,c.shifthours,c.basefare,c.additionalcharges,c.orders,c.fuelcharge,a.status,
a.applocationid,d.locationname as applocation,d.logseconds
FROM app_users a
INNER JOIN ridersettings b ON a.userid=b.userid
INNER JOIN ridershifts c on b.shiftid=c.shiftid
INNER JOIN app_location d on a.applocationid=d.applocationid
WHERE a.userid=?`
utils.Logger.Debugf("Query executed for userid: %d", userid)
db.DB.Raw(q1, userid).Find(&data)
return data
}
func GetRiderInfov2(userid int) models.RiderInfo {
var data models.RiderInfo
q1 := `SELECT a.userid,a.firstname,a.lastname, CONCAT(a.firstname,' ',a.lastname) as fullname,
a.partnerid,a.configid,a.contactno,
a.address,a.suburb,a.city,a.state,a.postcode,a.latitude,a.longitude,a.tenantid,
b.identificationno,b.vehicleno,b.vehiclename,b.licenseno,b.insuranceno,b.insurancedate,
c.shiftid,c.starttime,c.endtime,c.shifthours,c.basefare,c.additionalcharges,c.orders,c.fuelcharge,a.status,
a.applocationid,d.locationname as applocation,d.logseconds
FROM app_users a
LEFT JOIN ridersettings b ON a.userid=b.userid
INNER JOIN ridershifts c ON a.shiftid=c.shiftid
INNER JOIN app_location d on a.applocationid=d.applocationid
WHERE a.userid=?`
utils.Logger.Debugf("Query executed for userid: %d", userid)
db.DB.Raw(q1, userid).Find(&data)
return data
}
func GetRiderDetails(userid int) models.RiderDetails {
var data models.RiderDetails
q1 := `SELECT a.*,b.*,c.* from app_users a
INNER JOIN ridersettings b ON a.userid=b.userid
LEFT JOIN ridershifts c ON b.shiftid=c.shiftid
WHERE a.userid=?`
db.DB.Raw(q1, userid).Find(&data)
return data
}
func GetAllRiders(aid, pid, uid int, status, keyword string, pageno, pagesize int) []models.RiderInfo {
var data []models.RiderInfo
var q1 string
var params []interface{}
const riders = `SELECT DISTINCT a.userid,a.firstname,a.lastname,
CONCAT(a.firstname,' ',a.lastname) AS username,
a.partnerid,a.configid,a.contactno,
a.address,a.suburb,a.city,a.state,a.postcode,a.latitude,a.longitude,a.name,a.capacity,a.rangekm,a.batterypercentage,a.type,
b.identificationno,b.vehicleno,b.vehiclename,b.licenseno,b.insuranceno,b.insurancedate,
c.shiftid,c.starttime,c.endtime,c.shifthours,c.basefare,c.additionalcharges,c.orders,
c.fuelcharge,a.status,a.applocationid,d.locationname AS applocation
FROM app_users a
INNER JOIN ridersettings b ON a.userid=b.userid
INNER JOIN ridershifts c ON b.shiftid=c.shiftid
INNER JOIN app_location d ON a.applocationid=d.applocationid
INNER JOIN app_locationconfig e ON a.applocationid=e.applocationid`
conditions := []string{"e.status='Active'"}
if aid != 0 {
conditions = append(conditions, "a.applocationid=?")
params = append(params, aid)
}
if pid != 0 {
conditions = append(conditions, "a.partnerid=?")
params = append(params, pid)
}
if uid != 0 {
conditions = append(conditions, "a.userid=?")
params = append(params, uid)
}
if status != "" {
conditions = append(conditions, "a.status=?")
params = append(params, status)
}
if keyword != "" {
k := "%" + strings.ToLower(keyword) + "%"
conditions = append(conditions,
`(LOWER(a.firstname) LIKE ?
OR LOWER(a.lastname) LIKE ?
OR LOWER(a.contactno) LIKE ?
OR LOWER(a.userid::text) LIKE ?)`)
params = append(params, k, k, k, k)
}
q1 = riders + " WHERE " + strings.Join(conditions, " AND ") + " ORDER BY a.userid DESC"
// Apply pagination only if pagesize > 0
if pagesize > 0 {
offset := (pageno - 1) * pagesize
q1 += " LIMIT ? OFFSET ?"
params = append(params, pagesize, offset)
}
db.DB.Raw(q1, params...).Find(&data)
return data
}
func GetPartners(aid, pid int) []models.Partnerinfo {
// onduty := c.Query("onduty")
var data []models.Partnerinfo
var q1 string
if pid != 0 {
q1 = `select partnerid,applocationid,partnertypeid,partnername,primarycontact,primaryemail,
contactno,address,suburb,state,city,partnerimage from partnerinfo where status='Active' and partnerid=?`
db.DB.Raw(q1, pid).Find(&data)
} else if aid != 0 {
q1 = `select partnerid,applocationid,partnertypeid,partnername,primarycontact,primaryemail,
contactno,address,suburb,state,city,partnerimage from partnerinfo where status='Active' and applocationid=?`
db.DB.Raw(q1, aid).Find(&data)
} else {
q1 = `select partnerid,applocationid,partnertypeid,partnername,primarycontact,primaryemail,
contactno,address,suburb,state,city,partnerimage from partnerinfo where status='Active'`
db.DB.Raw(q1).Find(&data)
}
return data
}
func GetLocationConfig(uid, cid int) []models.Locationconfigs {
var data []models.Locationconfigs
q1 := `SELECT Distinct a.applocationid,a.locationname FROM app_location a
inner join app_locationconfig b ON a.applocationid=b.applocationid
INNER JOIN app_users c ON a.applocationid=c.applocationid
INNER JOIN app_roles d ON d.roleid=c.roleid
WHERE b.status='Active' and b.userid=? ORDER BY applocationid ASC`
db.DB.Raw(q1, uid).Find(&data)
return data
}
func GetPartnerUsers(aid int) []models.UserInfo {
var data []models.UserInfo
q1 := `SELECT a.userid,a.authname,a.email,a.configid,a.roleid,a.authmode,a.contactno,a.firstname,a.lastname,concat(a.firstname,' ',a.lastname) as fullname,
a.userfcmtoken,a.pin,a.deviceid,a.devicetype,a.tenantid,a.applocationid,a.partnerid
from app_users a where configid=5 and a.applocationid=?`
db.DB.Raw(q1, aid).Find(&data)
return data
}
func GetAdmintoken(pid, uid, tid, aid int) models.AdminToken {
var data models.AdminToken
var q1 string
if tid != 0 && pid != 0 {
q1 = `SELECT a.userid,a.userfcmtoken, b.closetime,b.opentime,c.tenantid
FROM app_users a
INNER JOIN partnerinfo b ON a.partnerid=b.partnerid
INNER JOIN tenants c ON a.userid=c.partneruserid
WHERE b.partnerid=? AND c.tenantid=?`
db.DB.Raw(q1, pid, tid).Find(&data)
} else if aid != 0 {
q1 = `SELECT distinct a.userid,a.userfcmtoken,b.notify,b.applocationid,c.opentime,c.closetime FROM app_users a
INNER JOIN app_locationconfig b ON a.userid=b.userid
INNER JOiN app_location c on a.applocationid= c.applocationid
WHERE b.notify='true' AND b.applocationid=?`
db.DB.Raw(q1, aid).Find(&data)
}
return data
}
func CreateRider(data models.Rider) error {
var userpools models.Userpools
tx := db.DB.Begin()
// ✅ FIX 1: Proper upsert for app_users
t1 := tx.Table("app_users").Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "email"}}, // REQUIRED
DoUpdates: clause.AssignmentColumns([]string{
"firstname",
"lastname",
"contactno",
"address",
"city",
"state",
}),
}).Create(&data)
if t1.Error != nil {
tx.Rollback()
return t1.Error
}
// ✅ FIX 2: Manually insert ridersettings (prevents ON CONFLICT error)
data.Ridersettings.Userid = data.Userid
data.Ridersettings.Partnerid = data.Partnerid
tRs := tx.Table("ridersettings").
Omit("riderid"). // 🚀 THIS FIXES YOUR ISSUE
Create(&data.Ridersettings)
if tRs.Error != nil {
tx.Rollback()
return tRs.Error
}
// ✅ Existing logic (unchanged)
userpools.Userid = data.Userid
userpools.Partnerid = data.Partnerid
userpools.Vehicleid = data.Ridersettings.Vehicleid
t2 := tx.Table("app_userpools").Create(&userpools)
if t2.Error != nil {
tx.Rollback()
return t2.Error
}
return tx.Commit().Error
}
func UpdateRiderSettings(data models.Rider) error {
tx := db.DB.Begin()
t1 := tx.Table("app_users").Where("userid=?", data.Userid).Updates(&data)
if t1.Error != nil {
tx.Rollback()
return t1.Error
}
t2 := tx.Table("ridersettings").Where("userid=?", data.Userid).Updates(&data.Ridersettings)
if t2.Error != nil {
tx.Rollback()
return t2.Error
}
err := tx.Commit().Error
if err != nil {
return err
}
return nil
}
// func UpdateRiderInfo(data models.Rider) error {
// tx := db.DB.Begin()
// t1 := tx.Table("app_users").Where("userid=?", data.Userid).Updates(&data)
// if t1.Error != nil {
// tx.Rollback()
// return t1.Error
// }
// err := tx.Commit().Error
// if err != nil {
// return err
// }
// return nil
// }
func UpdateRiderInfo(data models.Rider) error {
tx := db.DB.Begin()
t1 := tx.Table("app_users").Where("userid=?", data.Userid).Updates(&data)
if t1.Error != nil {
tx.Rollback()
return t1.Error
}
t2 := tx.Table("ridersettings").Where("userid=?", data.Userid).Updates(&data.Ridersettings)
if t2.Error != nil {
tx.Rollback()
return t2.Error
}
err := tx.Commit().Error
if err != nil {
return err
}
return nil
}
func CreateRiderShift(data models.Ridershifts) error {
tx := db.DB.Begin()
t1 := tx.Table("ridershifts").Create(&data)
if t1.Error != nil {
tx.Rollback()
return t1.Error
}
err := tx.Commit().Error
if err != nil {
return err
}
return nil
}
func CreateRiderLog(data models.Riderlogs) error {
tx := db.DB.Begin()
t1 := tx.Table("riderlogs").Create(&data)
if t1.Error != nil {
tx.Rollback()
return t1.Error
}
t2 := tx.Table("app_userpools").Where("userid=?", data.Userid).UpdateColumn("onduty", 1)
if t2.Error != nil {
tx.Rollback()
return t2.Error
}
err := tx.Commit().Error
if err != nil {
return err
}
return nil
}
func GetBreaklog(bid int) models.Riderbreaks {
var data models.Riderbreaks
q1 := "select * from riderbreaks where breakid=?"
db.DB.Raw(q1, bid).Find(&data)
return data
}
func CreateBreakLog(data models.Riderbreaks) (int, error) {
tx := db.DB.Begin()
t1 := tx.Table("riderbreaks").Create(&data)
if t1.Error != nil {
tx.Rollback()
return 0, t1.Error
}
t2 := tx.Table("app_userpools").Where("userid=?", data.Userid).UpdateColumn("onduty", 0)
if t2.Error != nil {
tx.Rollback()
return 0, t2.Error
}
err := tx.Commit().Error
if err != nil {
return 0, err
}
return data.Breakid, nil
}
func UpdateBreakLog(data models.Riderbreaks) error {
tx := db.DB.Begin()
t1 := tx.Table("riderbreaks").Where("breakid=?", data.Breakid).Updates(&data)
if t1.Error != nil {
tx.Rollback()
return t1.Error
}
t2 := tx.Table("app_userpools").Where("userid=?", data.Userid).UpdateColumn("onduty", 1)
if t2.Error != nil {
tx.Rollback()
return t2.Error
}
err := tx.Commit().Error
if err != nil {
return err
}
return nil
}
func UpdateRiderLog(data models.Riderlogs) error {
tx := db.DB.Begin()
t1 := tx.Table("riderlogs").Where("userid=?", data.Userid).Updates(&data)
if t1.Error != nil {
tx.Rollback()
return t1.Error
}
t2 := tx.Table("app_userpools").Where("userid=?", data.Userid).UpdateColumn("onduty", 0)
if t2.Error != nil {
tx.Rollback()
return t2.Error
}
err := tx.Commit().Error
if err != nil {
return err
}
return nil
}
func GetRiderLog(uid int) (models.Riderlogs, error) {
var data models.Riderlogs
q1 := `SELECT a.*,b.onduty from riderlogs a
INNER JOIN app_userpools b ON a.userid=b.userid WHERE a.userid=? and a.logdate::date=CURRENT_DATE`
err := db.DB.Raw(q1, uid).Find(&data).Error
if err != nil {
return data, err
}
return data, nil
}
func GetRiderLogs(pid, aid int, fdate, tdate, keyword string) []models.RiderlogDetails {
var data []models.RiderlogDetails
var q1 string
var params []interface{}
if pid != 0 {
if fdate != "" && tdate != "" {
q1 = `SELECT a.*,b.*,CONCAT(b.firstname,' ',b.lastname) AS username,
(SELECT SUM(breakhours) FROM riderbreaks WHERE logid = a.logid AND userid = a.userid) as breakhours
FROM riderlogs a
INNER JOIN app_users b ON a.userid=b.userid
WHERE a.partnerid = ?
AND a.logdate::date between ? and ?`
params = append(params, pid, fdate, tdate)
} else {
q1 = `SELECT a.*,b.*,CONCAT(b.firstname,' ',b.lastname) AS username,
(SELECT SUM(breakhours) FROM riderbreaks WHERE logid = a.logid AND userid = a.userid) as breakhours
FROM riderlogs a
INNER JOIN app_users b ON a.userid=b.userid
WHERE a.partnerid = ?
AND a.logdate::date=CURRENT_DATE`
params = append(params, pid)
}
} else if aid != 0 {
if fdate != "" && tdate != "" {
q1 = `SELECT a.*,b.*,CONCAT(b.firstname,' ',b.lastname) AS username,
(SELECT SUM(breakhours) FROM riderbreaks WHERE logid = a.logid AND userid = a.userid) as breakhours
FROM riderlogs a
INNER JOIN app_users b ON a.userid=b.userid
WHERE b.applocationid = ?
AND a.logdate::date between ? and ?`
params = append(params, aid, fdate, tdate)
} else {
q1 = `SELECT a.*,b.*,CONCAT(b.firstname,' ',b.lastname) AS username,
(SELECT SUM(breakhours) FROM riderbreaks WHERE logid = a.logid AND userid = a.userid) as breakhours
FROM riderlogs a
INNER JOIN app_users b ON a.userid=b.userid
WHERE b.applocationid = ?
AND a.logdate::date=CURRENT_DATE`
params = append(params, aid)
}
} else {
if fdate != "" && tdate != "" {
q1 = `SELECT a.*,b.*,CONCAT(b.firstname,' ',b.lastname) AS username,
(SELECT SUM(breakhours) FROM riderbreaks WHERE logid = a.logid AND userid = a.userid) as breakhours
FROM riderlogs a
INNER JOIN app_users b ON a.userid=b.userid
WHERE a.logdate::date between ? and ?`
params = append(params, fdate, tdate)
} else {
q1 = `SELECT a.*,b.*,CONCAT(b.firstname,' ',b.lastname) AS username,
(SELECT SUM(breakhours) FROM riderbreaks WHERE logid = a.logid AND userid = a.userid) as breakhours
FROM riderlogs a
INNER JOIN app_users b ON a.userid=b.userid
WHERE a.logdate::date=CURRENT_DATE`
}
}
if keyword != "" {
k := "%" + strings.ToLower(keyword) + "%"
q1 += `
AND (
LOWER(b.firstname) LIKE ? OR
LOWER(b.lastname) LIKE ? OR
LOWER(CONCAT(b.firstname,' ',b.lastname)) LIKE ? OR
b.userid LIKE ?
)`
params = append(params, k, k, k, keyword)
}
// Finish query
q1 += " ORDER BY logid ASC"
utils.Logger.Debugf("Query executed for RiderLogs")
db.DB.Raw(q1, params...).Find(&data)
return data
}
func GetRiderOrderCount(uid int) (int, error) {
var dc int
// q1 = `SELECT COUNT(*) as deliverycount FROM deliveryqueues WHERE queuestatus=0 and userid=` + strconv.Itoa(uid)
q1 := `SELECT COUNT(*) as deliverycount FROM deliveries
WHERE orderstatus IN('pending','accepted','arrived','picked') and userid=?`
db.DB.Raw(q1, uid).Find(&dc)
return dc, nil
}
func CreateBreakLogv1(data models.Riderbreaks) (int, error) {
if db.Rdb == nil {
return 0, errors.New("Redis client not initialized")
}
id, err := db.Rdb.Incr(db.Ctx, "riderbreaks:counter").Result()
if err != nil {
utils.Logger.Errorf("Redis INCR error: %v", err)
return 0, errors.New("failed to generate break ID")
}
data.Breakid = int(id)
jsonData, err := json.Marshal(data)
if err != nil {
utils.Logger.Errorf("JSON marshal error: %v", err)
return 0, errors.New("failed to serialize break data")
}
if err := db.Rdb.RPush(db.Ctx, "riderbreaks", jsonData).Err(); err != nil {
utils.Logger.Errorf("Redis RPush error: %v", err)
return 0, errors.New("failed to store break in Redis")
}
tx := db.DB.Begin()
if err := updateOnduty(tx, data.Userid); err != nil {
tx.Rollback()
return 0, err
}
if err := tx.Commit().Error; err != nil {
utils.Logger.Errorf("MySQL commit error: %v", err)
return 0, errors.New("failed to update onduty")
}
return data.Breakid, nil
}
func updateOnduty(tx *gorm.DB, userid int) error {
// Try update
result := tx.Table("app_userpools").
Where("userid = ?", userid).
UpdateColumn("onduty", 0)
if result.Error != nil {
return result.Error
}
// if result.RowsAffected == 0 {
// // INSERT instead of update
// err := tx.Table("app_userpools").Create(map[string]interface{}{
// "userid": userid,
// "onduty": 0,
// }).Error
// if err != nil {
// return errors.New("failed to insert onduty row: " + err.Error())
// }
// }
return nil
}
func GetBreaklogv1(breakID int) models.Riderbreaks {
if db.Rdb == nil {
return models.Riderbreaks{}
}
vals, err := db.Rdb.LRange(db.Ctx, "riderbreaks", 0, -1).Result()
if err != nil {
utils.Logger.Errorf("Redis LRange error: %v", err)
return models.Riderbreaks{}
}
var data models.Riderbreaks
for _, v := range vals {
var item models.Riderbreaks
if err := json.Unmarshal([]byte(v), &item); err != nil {
continue
}
if item.Breakid == breakID {
data = item
break
}
}
return data
}
func UpdateBreakLogv1(data models.Riderbreaks) error {
key := "riderbreaks"
values, err := db.Rdb.LRange(db.Ctx, key, 0, -1).Result()
if err != nil {
return errors.New("failed to read Redis list: " + err.Error())
}
if len(values) == 0 {
return errors.New("no data found in Redis list " + key)
}
var foundIndex int = -1
for i, val := range values {
var rb models.Riderbreaks
if err := json.Unmarshal([]byte(val), &rb); err != nil {
continue
}
if rb.Breakid == data.Breakid {
foundIndex = i
break
}
}
if foundIndex == -1 {
return errors.New("Break ID " + strconv.Itoa(data.Breakid) + " not found in Redis")
}
jsonData, err := json.Marshal(data)
if err != nil {
return errors.New("Redis marshal failed: " + err.Error())
}
if err := db.Rdb.LSet(db.Ctx, key, int64(foundIndex), jsonData).Err(); err != nil {
return errors.New("Redis LSET failed: " + err.Error())
}
tx := db.DB.Begin()
t2 := tx.Table("app_userpools").
Where("userid = ?", data.Userid).
UpdateColumn("onduty", 1)
if t2.Error != nil {
tx.Rollback()
return t2.Error
}
if err := tx.Commit().Error; err != nil {
return err
}
return nil
}
func GetRiderWeeklyKms(uid int) (*models.RiderWeeklyKms, error) {
var result []models.RiderKms
var overallKms float64
// 🔥 SAFE + POSTGRES COMPATIBLE QUERY
q1 := `
SELECT
TRIM(TO_CHAR(deliverydate, 'Dy')) AS day,
ROUND(
COALESCE(SUM(NULLIF(riderkms, '')::numeric), 0),
2) AS kms
FROM deliveries
WHERE userid = ?
AND deliverydate >= CURRENT_DATE - INTERVAL '7 days'
GROUP BY EXTRACT(DOW FROM deliverydate), TRIM(TO_CHAR(deliverydate, 'Dy'))
ORDER BY EXTRACT(DOW FROM deliverydate);
`
q2 := `
SELECT
ROUND(
COALESCE(SUM(NULLIF(riderkms, '')::numeric), 0),
2) AS overallkms
FROM deliveries
WHERE userid = ?;
`
// Execute queries
if err := db.DB.Raw(q1, uid).Scan(&result).Error; err != nil {
return nil, err
}
if err := db.DB.Raw(q2, uid).Scan(&overallKms).Error; err != nil {
return nil, err
}
// Map results
dayMap := make(map[string]float64)
for _, r := range result {
dayMap[r.Day] = r.Kms
}
// Ensure all 7 days
allDays := []string{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}
var final []models.RiderKms
var totalKms float64
for _, d := range allDays {
kms := dayMap[d]
final = append(final, models.RiderKms{
Day: d,
Kms: kms,
})
totalKms += kms
}
return &models.RiderWeeklyKms{
Details: final,
TotalKms: totalKms,
OverallKms: overallKms,
}, nil
}
func CreateRiderLogv1(data models.Riderlogs) error {
ctx := context.Background()
pong, err := db.Rdb.Ping(ctx).Result()
utils.Logger.Infof("[Redis] Ping: %s Error: %v", pong, err)
if err != nil {
return errors.New("redis connection error: " + err.Error())
}
tx := db.DB.Begin()
if err := tx.Table("riderlogs").Create(&data).Error; err != nil {
tx.Rollback()
return errors.New("mysql insert error (riderlogs): " + err.Error())
}
utils.Logger.Infof("[MySQL] Generated logid: %d", data.Logid)
if err := tx.Table("app_userpools").
Where("userid = ?", data.Userid).
UpdateColumn("onduty", 1).Error; err != nil {
tx.Rollback()
return errors.New("mysql update error (onduty): " + err.Error())
}
if err := tx.Commit().Error; err != nil {
return errors.New("mysql commit error: " + err.Error())
}
jsonData, err := json.Marshal(data)
if err != nil {
return errors.New("failed to marshal redis data: " + err.Error())
}
key := "riderlogs"
pushResult := db.Rdb.RPush(ctx, key, jsonData)
if pushResult.Err() != nil {
return errors.New("failed to push rider log to redis: " + pushResult.Err().Error())
}
insertedCount, _ := pushResult.Result()
utils.Logger.Infof("[Redis] RPush OK — key=%s, list length now=%d", key, insertedCount)
val, err := db.Rdb.LRange(ctx, key, -1, -1).Result()
if err != nil {
utils.Logger.Errorf("[Redis] LRange check error: %v", err)
} else {
utils.Logger.Infof("[Redis] Last inserted value: %v", val)
}
return nil
}
func UpdateRiderLogv1(data models.Riderlogs) error {
ctx := context.Background()
key := "riderlogs"
// See comment in GetRiderLogsv1: riderlogs is an unbounded list, full scans
// need more than the client's default 3s ReadTimeout.
logs, err := db.Rdb.WithTimeout(15 * time.Second).LRange(ctx, key, 0, -1).Result()
if err != nil {
return errors.New("failed to fetch logs from redis: " + err.Error())
}
found := false
for i, item := range logs {
var existing models.Riderlogs
if err := json.Unmarshal([]byte(item), &existing); err != nil {
continue
}
if existing.Userid == data.Userid {
found = true
existingVal := reflect.ValueOf(&existing).Elem()
dataVal := reflect.ValueOf(&data).Elem()
for i := 0; i < dataVal.NumField(); i++ {
field := dataVal.Field(i)
if !isZero(field) { // only update if non-zero
existingVal.Field(i).Set(field)
}
}
updatedJSON, _ := json.Marshal(existing)
if err := db.Rdb.LSet(ctx, key, int64(i), updatedJSON).Err(); err != nil {
return errors.New("failed to update redis: " + err.Error())
}
break
}
}
if !found {
return errors.New("rider log for userid " + strconv.Itoa(data.Userid) + " not found in redis")
}
tx := db.DB.Begin()
t1 := tx.Table("app_userpools").
Where("userid = ?", data.Userid).
UpdateColumn("onduty", 0)
if t1.Error != nil {
tx.Rollback()
return errors.New("mysql update error: " + t1.Error.Error())
}
if err := tx.Commit().Error; err != nil {
return errors.New("mysql commit error: " + err.Error())
}
return nil
}
func isZero(v reflect.Value) bool {
switch v.Kind() {
case reflect.String:
return v.Len() == 0
case reflect.Int, reflect.Int64, reflect.Float64:
return v.Int() == 0
case reflect.Ptr, reflect.Interface:
return v.IsNil()
default:
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
}
}
func CreateRiderSupport(data models.RiderSupport) error {
tx := db.DB.Begin()
t1 := tx.Table("ridersupport").Create(&data)
if t1.Error != nil {
tx.Rollback()
return t1.Error
}
err := tx.Commit().Error
if err != nil {
return err
}
return nil
}
func GetRiderSupport(uid int) ([]models.RiderSupport, error) {
var data []models.RiderSupport
q1 := "SELECT * FROM ridersupport WHERE userid=? ORDER BY ridersupportid DESC"
err := db.DB.Raw(q1, uid).Scan(&data).Error
if err != nil {
return data, err
}
return data, nil
}
func GetRiderLogsv1(fromdate, todate, keyword string, partnerid int) ([]models.Riderlogsv1, error) {
ctx := context.Background()
results := []models.Riderlogsv1{}
// riderlogs is an unbounded, ever-growing list (900K+ entries as of writing).
// A full-list LRANGE routinely takes several seconds, well past the client's
// default 3s ReadTimeout, so this call gets a longer timeout scoped to just
// this request instead of loosening the timeout for every Redis call.
redisList, err := db.Rdb.WithTimeout(15 * time.Second).LRange(ctx, "riderlogs", 0, -1).Result()
if err != nil {
return nil, errors.New("redis lrange error: " + err.Error())
}
if len(redisList) == 0 {
return results, nil
}
from, _ := time.Parse("2006-01-02", fromdate)
to, _ := time.Parse("2006-01-02", todate)
to = to.Add(24 * time.Hour)
keyword = strings.ToLower(keyword)
last := make(map[int]models.Riderlogsv1)
for _, item := range redisList {
idx := strings.Index(item, `"logdate":"`)
if idx == -1 {
continue
}
start := idx + len(`"logdate":"`)
end := start + 19
if end > len(item) {
continue
}
logDateStr := item[start:end]
logTime, err := time.Parse("2006-01-02 15:04:05", logDateStr)
if err != nil {
continue
}
if logTime.Before(from) || logTime.After(to) {
continue
}
var log models.Riderlogsv1
if err := json.Unmarshal([]byte(item), &log); err != nil {
continue
}
if partnerid > 0 && log.Partnerid != partnerid {
continue
}
if keyword != "" {
if !strings.Contains(strings.ToLower(log.Firstname), keyword) &&
!strings.Contains(strings.ToLower(log.Lastname), keyword) &&
!strings.Contains(strings.ToLower(log.Username), keyword) {
continue
}
}
if prev, ok := last[log.Userid]; ok {
prevTime, _ := time.Parse("2006-01-02 15:04:05", prev.Logdate)
if logTime.After(prevTime) {
last[log.Userid] = log
}
} else {
last[log.Userid] = log
}
}
for _, v := range last {
results = append(results, v)
}
return results, nil
}
func GetUserByID(userid int) (models.User, error) {
var user models.User
q := `SELECT userid, firstname, lastname FROM app_users WHERE userid=? LIMIT 1`
err := db.DB.Raw(q, userid).Scan(&user).Error
return user, err
}
func GetUserCached(userid int) (models.User, error) {
ctx := context.Background()
var user models.User
key := "user:info:" + strconv.Itoa(userid)
val, err := db.Rdb.Get(ctx, key).Result()
if err == nil {
json.Unmarshal([]byte(val), &user)
return user, nil
}
q := `SELECT *
FROM app_users
WHERE userid=? LIMIT 1`
if err := db.DB.Raw(q, userid).Scan(&user).Error; err != nil {
return user, err
}
b, _ := json.Marshal(user)
db.Rdb.Set(ctx, key, b, 6*time.Hour)
return user, nil
}
func GetAllRidersSummary(aid, pid, uid int, keyword string) models.RidersSummary {
var summary models.RidersSummary
var conditions []string
const baseQry = `
SELECT
COUNT(DISTINCT CASE WHEN a.status='Active' THEN a.userid END) AS active,
COUNT(DISTINCT CASE WHEN a.status='Inactive' THEN a.userid END) AS inactive,
COUNT(DISTINCT a.userid) AS total
FROM app_users a
INNER JOIN ridersettings b ON a.userid=b.userid
INNER JOIN ridershifts c ON b.shiftid=c.shiftid
INNER JOIN app_location d ON a.applocationid=d.applocationid
INNER JOIN app_locationconfig e
ON a.applocationid=e.applocationid AND e.status='Active'
WHERE 1=1
`
var params []interface{}
if aid != 0 {
conditions = append(conditions, "a.applocationid=?")
params = append(params, aid)
}
if pid != 0 {
conditions = append(conditions, "a.partnerid=?")
params = append(params, pid)
}
if uid != 0 {
conditions = append(conditions, "a.userid=?")
params = append(params, uid)
}
if keyword != "" {
k := "%" + strings.ToLower(keyword) + "%"
conditions = append(conditions, "(LOWER(a.firstname) LIKE ? OR LOWER(a.lastname) LIKE ? OR LOWER(a.contactno) LIKE ? OR LOWER(CAST(a.userid AS CHAR)) LIKE ?)")
params = append(params, k, k, k, k)
}
q := baseQry
if len(conditions) > 0 {
q += " AND " + strings.Join(conditions, " AND ")
}
utils.Logger.Debugf("SUMMARY QUERY Executed")
db.DB.Raw(q, params...).Scan(&summary)
return summary
}
type ActiveRidersSummary struct {
Total int `json:"total"`
}
func GetActiveRidersSummary(partnerid, aid, uid int, keyword string) ActiveRidersSummary {
var summary ActiveRidersSummary
var conditions []string
const baseQry = `
SELECT
COUNT(DISTINCT a.userid) AS total
FROM app_users a
INNER JOIN app_userpools b ON a.userid = b.userid
INNER JOIN ridersettings c ON a.userid = c.userid
INNER JOIN ridershifts d ON c.shiftid = d.shiftid
INNER JOIN (
SELECT r1.*
FROM riderlogs r1
INNER JOIN (
SELECT userid, MAX(logdate) AS max_logdate
FROM riderlogs
WHERE logdate::date = CURRENT_DATE
GROUP BY userid
) r2
ON r1.userid = r2.userid AND r1.logdate = r2.max_logdate
) e ON a.userid = e.userid
INNER JOIN app_location f ON a.applocationid = f.applocationid
INNER JOIN app_locationconfig g ON g.applocationid = f.applocationid
WHERE 1=1
`
conditions = append(conditions,
"a.configid = 6",
"a.status = 'Active'",
"b.onduty = 1",
"e.logdate::date = CURRENT_DATE",
"e.logstatus = 0",
)
var params []interface{}
if aid != 0 {
conditions = append(conditions, "a.applocationid = ?")
params = append(params, aid)
}
if partnerid != 0 {
conditions = append(conditions, "a.partnerid = ?")
params = append(params, partnerid)
}
if uid != 0 {
conditions = append(conditions, "a.userid = ?")
params = append(params, uid)
}
if keyword != "" {
k := "%" + strings.ToLower(keyword) + "%"
conditions = append(conditions, "(LOWER(a.firstname) LIKE ? OR LOWER(a.lastname) LIKE ? OR LOWER(a.contactno) LIKE ? OR LOWER(CAST(a.userid AS CHAR)) LIKE ?)")
params = append(params, k, k, k, k)
}
q := baseQry + " AND " + strings.Join(conditions, " AND ")
utils.Logger.Debugf("ACTIVE SUMMARY QUERY Executed")
db.DB.Raw(q, params...).Scan(&summary)
return summary
}