daily merchant app login fix

This commit is contained in:
2026-09-11 11:10:25 +05:30
parent e7577fe0cf
commit 8cc567c89b
4 changed files with 47 additions and 15 deletions

View File

@@ -101,4 +101,11 @@ type TenantUserInfo struct {
Categoryname string `json:"categoryname"` Categoryname string `json:"categoryname"`
Subcategoryid int `json:"subcategoryid"` Subcategoryid int `json:"subcategoryid"`
Issuperadmin bool `json:"issuperadmin"` Issuperadmin bool `json:"issuperadmin"`
// Carried so this response is a SUPERSET of what `UserInfo` returned.
// `/mob/users/tenant/login` used to answer with UserInfo, and a shipped app
// may read any of these three; dropping them while repointing the route
// would have been a breaking change disguised as a fix.
Shiftid int `json:"shiftid"`
Shiftname string `json:"shiftname"`
Status string `json:"status"`
} }

View File

@@ -14,7 +14,6 @@ type UserRepository interface {
GetUserByID(uid int) (models.UserInfo, error) GetUserByID(uid int) (models.UserInfo, error)
Login(user models.User) (models.UserInfo, error) Login(user models.User) (models.UserInfo, error)
FindUserID(authname, contactno string, configid int) (int, error) FindUserID(authname, contactno string, configid int) (int, error)
GetTenantUserByID(userid int) (models.TenantUserInfo, error)
UpdateStaff(user models.User) error UpdateStaff(user models.User) error
GetUserByAuthname(authname string, configid int) (int, string, string) GetUserByAuthname(authname string, configid int) (int, string, string)
GetUserByContactNo(contactno string, configid int) (int, string, string) GetUserByContactNo(contactno string, configid int) (int, string, string)
@@ -188,18 +187,7 @@ func (r *userRepository) FindUserID(authname, contactno string, configid int) (i
return uid, nil return uid, nil
} }
func (r *userRepository) GetTenantUserByID(userid int) (models.TenantUserInfo, error) {
var info models.TenantUserInfo
query := `SELECT a.userid, a.authname, a.contactno, a.tenantid, t.tenantname
FROM app_users a
LEFT JOIN tenants t ON a.tenantid = t.tenantid
WHERE a.userid = ?`
if err := r.db.Raw(query, userid).Scan(&info).Error; err != nil {
return info, err
}
return info, nil
}
func (r *userRepository) UpdateStaff(user models.User) error { func (r *userRepository) UpdateStaff(user models.User) error {
return r.db.Table("app_users").Where("userid = ?", user.Userid).Updates(&user).Error return r.db.Table("app_users").Where("userid = ?", user.Userid).Updates(&user).Error
@@ -237,6 +225,15 @@ func (r *userRepository) UpdateFCMToken(userid int, token string) error {
return r.db.Exec(query, token, userid).Error return r.db.Exec(query, token, userid).Error
} }
// The one tenant-user read. There used to be two.
//
// A `GetTenantUserByID` sat beside this — one capital letter apart, twenty-eight
// columns short, selecting only userid, authname, contactno, tenantid and
// tenantname. `TenantLogin` called that one, so the mobile login it served
// answered with a record whose name, branch, region and coordinates were blank,
// and the route was quietly pointed at a different handler to work around it.
// Deleted rather than documented: two functions this similar, where picking the
// wrong one fails silently, is a trap and not an API.
func (r *userRepository) GetTenantUserById(userid int) models.TenantUserInfo { func (r *userRepository) GetTenantUserById(userid int) models.TenantUserInfo {
var info models.TenantUserInfo var info models.TenantUserInfo
@@ -252,12 +249,14 @@ func (r *userRepository) GetTenantUserById(userid int) models.TenantUserInfo {
b.partnerid,b.moduleid,b.categoryid as categoryid,b.subcategoryid as subcategoryid, b.partnerid,b.moduleid,b.categoryid as categoryid,b.subcategoryid as subcategoryid,
b.applocationid,b.tenantname,b.address as tenantaddress,b.state as tenantstate,b.city as tenantcity, b.applocationid,b.tenantname,b.address as tenantaddress,b.state as tenantstate,b.city as tenantcity,
b.postcode as tenantpostcode,b.latitude as tenantlat,b.longitude as tenantlong,c.locationname AS applocation, b.postcode as tenantpostcode,b.latitude as tenantlat,b.longitude as tenantlong,c.locationname AS applocation,
c.latitude as applatitude,c.longitude as applongitude,c.radius as appradius, d.categoryname, e.locationname c.latitude as applatitude,c.longitude as applongitude,c.radius as appradius, d.categoryname, e.locationname,
a.shiftid, concat(f.starttime, ' - ', f.endtime) as shiftname, a.status
from app_users a from app_users a
LEFT JOIN tenants b ON a.tenantid=b.tenantid LEFT JOIN tenants b ON a.tenantid=b.tenantid
LEFT JOIN app_location c on c.applocationid=b.applocationid LEFT JOIN app_location c on c.applocationid=b.applocationid
LEFT JOIN app_category d ON b.categoryid=d.categoryid LEFT JOIN app_category d ON b.categoryid=d.categoryid
LEFT JOIN tenantlocations e ON a.locationid=e.locationid LEFT JOIN tenantlocations e ON a.locationid=e.locationid
LEFT JOIN ridershifts f ON a.shiftid = f.shiftid
WHERE a.userid = ? WHERE a.userid = ?
` `

View File

@@ -18,7 +18,12 @@ func RegisterUserRoutes(api fiber.Router, f *facade.Facade) {
users.Delete("/delete", f.UserController.DeleteUser) users.Delete("/delete", f.UserController.DeleteUser)
users = api.Group("/v1/mob/users") users = api.Group("/v1/mob/users")
users.Post("/tenant/login", f.UserController.Login) // `TenantLogin`, not `Login`. The route is named for the tenant and the app
// needs the shop with it — its name, address, category, branch and the
// superadmin flag — none of which `UserInfo` carries. `TenantUserInfo` now
// also carries the shiftid, shiftname and status that `Login` returned, so
// this response is a superset of the old one and no client loses a field.
users.Post("/tenant/login", f.UserController.TenantLogin)
users.Get("/getusers", f.UserController.GetUserInfo) users.Get("/getusers", f.UserController.GetUserInfo)
users.Post("/create", f.UserController.CreateUser) users.Post("/create", f.UserController.CreateUser)
users.Put("/update", f.UserController.UpdateStaff) users.Put("/update", f.UserController.UpdateStaff)

View File

@@ -51,7 +51,28 @@ func (s *userService) TenantLogin(user models.User) (models.TenantUserInfo, erro
return models.TenantUserInfo{}, errors.New("user not found") return models.TenantUserInfo{}, errors.New("user not found")
} }
return s.repo.GetTenantUserByID(uid) // `GetTenantUserById`, NOT `GetTenantUserByID`.
//
// The two differ by one letter and by twenty-eight columns. The capital-ID
// one selects five — userid, authname, contactno, tenantid, tenantname —
// so this function used to answer with a record whose name, branch, region
// and coordinates were all blank. That is why routing
// `/mob/users/tenant/login` at it was never as simple as swapping the
// handler: the app would have received a mostly-empty object.
//
// The lowercase-d one is the query `AppLogin` and `TenantWebLogin` already
// use, and it fills the whole record. It is now the only one anything calls.
//
// The FCM token is stored here rather than left to the repository, because
// the full read does not write. Only a real token is stored: a login that
// omits it must not wipe the device already registered, which is exactly
// what "userfcmtoken": "your_fcm_token_here" did to a live account during
// this investigation.
if strings.TrimSpace(user.Userfcmtoken) != "" {
_ = s.repo.UpdateUserFcmToken(uid, user.Userfcmtoken)
}
return s.repo.GetTenantUserById(uid), nil
} }
func (s *userService) UpdateStaff(user models.User) error { func (s *userService) UpdateStaff(user models.User) error {