package user import ( "fmt" "nCovTrack-Backend/global" "nCovTrack-Backend/models" "nCovTrack-Backend/service/notify" "nCovTrack-Backend/utils" "strings" "time" "github.com/golang-jwt/jwt/v4" "github.com/google/uuid" ) const ( EMAIL_CODE_REDIS_KEY = "verify_code_" ) // Login if login success, will return token func Login(user map[string]interface{}) (token string) { account := user["account"].(string) var queryMap []map[string]interface{} if strings.Contains(account, "@") { queryMap = append(queryMap, map[string]interface{}{"email": account}) } else { queryMap = append(queryMap, map[string]interface{}{"phone": account}) } userInfo := models.Get[models.BackUser](queryMap) if userInfo == nil { return "" } if userInfo["approver"].(int) <= 0 { return "" } if !utils.PasswordCompare(user["password"].(string), userInfo["password"].(string)) { return "" } claims := jwt.MapClaims{ "id": userInfo["id"], "username": userInfo["username"], "email": userInfo["email"], "region": userInfo["region"], "role": userInfo["role"], } return utils.GenerateToken(claims) } // Register user register, user can use account after approved func Register(user map[string]interface{}) bool { user["password"] = utils.PasswordEncrypt(user["password"].(string)) user["approver"] = 0 colMap := models.MapJ2c[models.BackUser](user, false) ok, rowsAffected := models.Upsert[models.BackUser](colMap) var sendRegion string if int(user["role"].(float64)) == global.ROLE_ID_MAP["ADMIN"] { sendRegion = strings.Split(user["region"].(string), " ")[0] } else { sendRegion = user["region"].(string) } notification := models.BackNotification{Time: time.Now(), Kind: "审批", Content: "有新的注册待审批"} sendInfo := models.SendInfo{Region: []string{sendRegion}, Channel: []int{0}, Notification: notification} notify.SendNotify(sendInfo) if !ok || rowsAffected == 0 { return false } return true } // ListRegister list the registers in the redis to be approved func ListRegister(claims models.TokenClaims) *[]map[string]interface{} { registers := []map[string]interface{}{} tx := global.Db.Model(new(models.BackUser)).Omit("password") if claims.Region == "" { // do nothing } else if !strings.Contains(claims.Region, " ") { // Province Admin approve city admin tx.Where("approver = 0 AND is_delete = 0 AND region LIKE ? AND role = ?", claims.Region+" %", global.ROLE_ID_MAP["ADMIN"]) registers = *models.ListByOrm(tx) } else { // City Admin approve workers and volunteers tx.Where("approver = 0 AND is_delete = 0 AND region = ? AND role in ?", claims.Region, []int{global.ROLE_ID_MAP["WORKER"], global.ROLE_ID_MAP["VOLUNTEER"]}) registers = *models.ListByOrm(tx) } return ®isters } // ListApprovedRegister list registers approved by the admin func ListApprovedRegister(claims models.TokenClaims) *[]map[string]interface{} { approvedRegisters := []map[string]interface{}{} tx := global.Db.Model(new(models.BackUser)).Omit("password").Where("approver in ? and is_delete = 0", []int{claims.ID, -claims.ID}) approvedRegisters = *models.ListByOrm(tx) return &approvedRegisters } // ApproveRegister approve a register func ApproveRegister(claims models.TokenClaims, email string, pass bool) bool { queryMap := []map[string]interface{}{{"email": email}} var approver int // Approver field status: // = 0: not approved // > 0: approver id, and registe successful // < 0: approver id, and registe failed if pass { approver = claims.ID } else { approver = -claims.ID } updateMap := map[string]interface{}{"approver": approver, "modify_time": time.Now()} ok, rowsAffected := models.Update[models.BackUser](queryMap, updateMap) if !ok || rowsAffected == 0 { return false } return true } // ChangePassword user change password, or user forgot password func ChangePassword(changePwd map[string]interface{}) bool { match := VerifyEmailCode(changePwd["email"].(string), changePwd["code"].(string))["match"].(bool) if !match { return false } newPassword := utils.PasswordEncrypt(changePwd["newPassword"].(string)) colMap := map[string]interface{}{ "id": 1.0, "password": newPassword, } models.BeforeSave(colMap, -1) delete(colMap, "id") delete(colMap, "modify_user") rowAffected := global.Db.Model(models.BackUser{}).Where("email = ?", changePwd["email"]).Updates(colMap).RowsAffected if rowAffected == 0 { return false } now := time.Now().Unix() global.Redis.HSet(global.CHANGEPWD_REDIS_KEY, changePwd["email"].(string), now) return true } // NoDuplicatePhoneOrEmail detect the phone or email is registered or not func NoDuplicatePhoneOrEmail(phone string, email string) bool { var queryMap []map[string]interface{} if phone != "" { queryMap = append(queryMap, map[string]interface{}{"phone": phone}) } if email != "" { queryMap = append(queryMap, map[string]interface{}{"email": email}) } return len(queryMap) != 0 && models.Count[models.BackUser](queryMap) == 0 } // SendEmailCode used to send email verify code func SendEmailCode(email string) bool { code := uuid.New().String()[0:6] text := fmt.Sprintf("Your Verify Code is :%s, Will Expire After 10 Minutes", code) subject := "nCovTrack Verify" // only set expired, not limit the frequency of use global.Redis.Set(EMAIL_CODE_REDIS_KEY+email, code, 10*time.Minute) return utils.SendEmail(subject, text, email) } // VerifyEmailCode use to verify user's verify code is correct or not func VerifyEmailCode(email string, code string) map[string]interface{} { verifyCode := global.Redis.Get(EMAIL_CODE_REDIS_KEY + email).Val() return map[string]interface{}{"match": verifyCode == code} }