package service import ( "crypto/sha1" "database/sql" "encoding/hex" "encoding/json" "errors" "fmt" "gopkg.in/guregu/null.v3" "log" "strings" "time" "gopkg.in/redis.v2" "xiaoniaokuaiyan.com/xiaoniao/config" "xiaoniaokuaiyan.com/xiaoniao/constants" dal "xiaoniaokuaiyan.com/xiaoniao/dal" "xiaoniaokuaiyan.com/xiaoniao/entity" server "xiaoniaokuaiyan.com/xiaoniao/server" util "xiaoniaokuaiyan.com/xiaoniao/util" ) const PASSWORD_KEY string = "2015bird2015assay2015" type UserService struct { server.UserService dal.IUser } /*var USER_NOT_EXISTS_ERROR = errors.New("user account not exists") var USER_ALREADY_EXISTS_ERROR = errors.New("account already regist") func getUser(userName string) (*entity.User, error) { db := util.GetSqlDB() strSql := "select ID, CUSTOM_MOBILE, CUSTOM_PASSWORD, custom_salt as CUSTOM_SALT from custom where custom_mobile = ? limit 1" var user entity.User err := db.Get(&user, strSql, userName) if err != nil { return nil, USER_NOT_EXISTS_ERROR } return &user, nil }*/ func (userService *UserService) Login(params map[string]string) (interface{}, error) { loginType := params["type"] mobile := params["mobile"] if loginType != "wx" && loginType != "zfb" && loginType != "trd" { if ok := util.IsMobile(mobile); !ok { return nil, errors.New("1::手机号码有误!") } } isNew := true switch loginType { case "normal": user, err := userService.IUser.Get(mobile) if err != nil { return nil, errors.New("3::用户不存在,请先注册") } password := params["password"] if password == "" { return nil, errors.New("2::密码不能为空") } dstPwd := encryptPassword(PASSWORD_KEY + password + user.PasswordSalt) if dstPwd != user.Password { return nil, errors.New("4::password incorrect") } case "vcode": vcode := params["vcode"] _, err := dal.DefaultSMSCodeDal.Get(mobile, vcode, int(constants.SMSCODE_LOGIN)) if err != nil { return nil, errors.New("5::验证码错误") } userTemp, err := userService._registUser(mobile, false) if err != nil { return nil, errors.New("regist failed") } if userTemp != nil { isNew = userTemp.IsNew } go func() { db := util.GetWriteSqlDB() db.Exec("update t_sms_code set is_used = 1 where mobile=? and code_type = ? and code=?", mobile, constants.SMSCODE_LOGIN, vcode) db.Exec("update t_custom set is_accept_up = 'Y' where mobile = ?;", mobile) unionId := params["unionId"] if len(unionId) > 0 { db.Exec("update t_custom set unionid = ? where mobile = ?;", unionId, mobile) } //20220530 保存三方openid if openidtrd, ok := params["openid_trd"]; ok && len(strings.TrimSpace(openidtrd)) > 0 { var extra = map[string]interface{}{} if userTemp.Extra.Valid { json.Unmarshal([]byte(userTemp.Extra.String), &extra) } var key = "openid_trd" extra[key] = openidtrd buf, _ := json.Marshal(extra) um := entity.User{ Mobile: mobile, Extra: string(buf), } _, err = userService.IUser.Update(&um) } }() case "wx": unionId := params["unionId"] user, err := userService.IUser.GetUserByUnionid(unionId) if err != nil { return nil, errors.New("3::用户不存在,请先注册") } isNew = false mobile = user.Mobile case "mp": //qz add 20201124 小程序注册, userTemp, err := userService._registUser(mobile, false) if err != nil { return nil, errors.New("regist failed") } if userTemp != nil { isNew = userTemp.IsNew } param := map[string]interface{}{} //var openid interface{} if userTemp.Extra.Valid { //&& userTemp.AccountType == 1 json.Unmarshal([]byte(userTemp.Extra.String), ¶m) //openid = param[openidKey] } param["openid_mp"] = params["openid_mp"] extraStr, err := json.Marshal(param) if err != nil { return nil, errors.New("7::type mp update extra error :" + err.Error()) } go func() { db := util.GetWriteSqlDB() db.Exec("update t_custom set is_accept_up = 'Y' where mobile = ?;", mobile) unionId := params["unionId"] if len(unionId) > 0 { db.Exec("update t_custom set unionid = ?,extra = ? where mobile = ?;", unionId, string(extraStr), mobile) } }() case "zfb": //支付宝小程序登录 zfbUserId := params["user_zfb_id"] user, err := userService.IUser.GetUserByUserId(zfbUserId) if err != nil { return nil, errors.New("3::用户不存在,请先注册") } isNew = false mobile = user.Mobile case "mini": //支付宝小程序注册 userTemp, err := userService._registUser(mobile, false) if err != nil { return nil, errors.New("regist failed") } if userTemp != nil { isNew = userTemp.IsNew } param := map[string]interface{}{} //var openid interface{} if userTemp.Extra.Valid { //&& userTemp.AccountType == 1 json.Unmarshal([]byte(userTemp.Extra.String), ¶m) //openid = param[openidKey] } param["user_zfb_id"] = params["user_zfb_id"] extraStr, err := json.Marshal(param) if err != nil { return nil, errors.New("7::type mp update extra error :" + err.Error()) } go func() { db := util.GetWriteSqlDB() db.Exec("update t_custom set is_accept_up = 'Y' where mobile = ?;", mobile) db.Exec("update t_custom set extra = ? where mobile = ?;", string(extraStr), mobile) }() case "trd": //20220530 参照wx 三方openid 登录 if openidtrd, ok := params["openid_trd"]; ok && len(strings.TrimSpace(openidtrd)) > 0 { user, err := userService.IUser.GetUserByUserId(openidtrd) if err != nil { return nil, errors.New("3::用户不存在,请先注册") } isNew = false mobile = user.Mobile } else { return nil, errors.New("3::缺少openid_trd") } default: return nil, errors.New("6::unknow login type") } /*config := nsq.NewConfig() producer, _ := nsq.NewProducer("127.0.0.1:4150", config) producer.Publish("api_logic", []byte("user "+mobile+"logged in")) */ user, _ := userService.IUser.Get(mobile) //todo save user login identifier to redis or somewhere else var client *redis.Client = util.GetRedis() client.Select(server.REDIS_API_AUTH_DB) sessionToken := server.GenerateSessionToken(fmt.Sprintf("%d-%s", user.Id, user.Mobile)) tokenBytes, err := json.Marshal(sessionToken) if err != nil { return nil, err } //sessionId := server.GenerateSessionId(user.Id, source) statusCmd := client.Set(sessionToken.Token, string(tokenBytes)) if err = statusCmd.Err(); err != nil { return nil, err } /*var exp int64 exp, err = config.IniConf.Section("server").Key("redis_api_session_expire").Int64() if err != nil || exp < 30 { exp = 60 } client.Expire(sessionToken.Token, time.Minute*time.Duration(exp))*/ param := map[string]interface{}{} var openid interface{} var openidKey = "openid" if k, ok := params["wx_type"]; ok && k == "mp" { openidKey = "openid_mp" } if user.Extra.Valid && user.AccountType == 1 { json.Unmarshal([]byte(user.Extra.String), ¶m) openid = param[openidKey] } var wxInfo interface{} = nil if params["openid"] != "" { wxInfo, err = getUserWxInfo(params["openid"]) if err != nil { return nil, err } } user.IsNew = isNew return map[string]interface{}{ "token": sessionToken.Token, "data": user, "openid": openid, "wxinfo": wxInfo, }, nil } func (userService *UserService) LoginV2(params map[string]string) (interface{}, error) { mobile := params["mobile"] if ok := util.IsMobile(mobile); !ok { return nil, errors.New("1::手机号码有误!") } vcode := params["vcode"] _, err := dal.DefaultSMSCodeDal.Get(mobile, vcode, int(constants.SMSCODE_LOGIN)) if err != nil { //return nil, errors.New("5::验证码错误") } isNewCustom, err := userService._registUserV2(params) if err != nil { return nil, errors.New("regist failed") } go func() { db := util.GetWriteSqlDB() db.Exec("update t_sms_code set is_used = 1 where mobile=? and code_type = ? and code=?", mobile, constants.SMSCODE_LOGIN, vcode) db.Exec("update t_custom set is_accept_up = 'Y' where mobile = ?;", mobile) }() /*config := nsq.NewConfig() producer, _ := nsq.NewProducer("127.0.0.1:4150", config) producer.Publish("api_logic", []byte("user "+mobile+"logged in")) */ user, _ := userService.IUser.Get(mobile) //todo save user login identifier to redis or somewhere else client := util.GetRedis() client.Select(server.REDIS_API_AUTH_DB) sessionToken := server.GenerateSessionToken(fmt.Sprintf("%d-%s", user.Id, user.Mobile)) tokenBytes, err := json.Marshal(sessionToken) if err != nil { return nil, err } //sessionId := server.GenerateSessionId(user.Id, source) statusCmd := client.Set(sessionToken.Token, string(tokenBytes)) if err = statusCmd.Err(); err != nil { return nil, err } /*var exp int64 exp, err = config.IniConf.Section("server").Key("redis_api_session_expire").Int64() if err != nil || exp < 30 { exp = 60 } client.Expire(sessionToken.Token, time.Minute*time.Duration(exp))*/ param := map[string]interface{}{} var openid interface{} var openidKey = "openid" if k, ok := params["wx_type"]; ok && k == "mp" { openidKey = "openid_mp" } if user.Extra.Valid && user.AccountType == 1 { json.Unmarshal([]byte(user.Extra.String), ¶m) openid = param[openidKey] } var wxInfo interface{} = nil if params["openid"] != "" { wxInfo, err = getUserWxInfo(params["openid"]) if err != nil { return nil, err } } return map[string]interface{}{ "token": sessionToken.Token, "data": user, "openid": openid, "wxinfo": wxInfo, "isNewCustom": isNewCustom, }, nil } func (userService *UserService) LoginCoupons(params map[string]string) (interface{}, error) { mobile := params["mobile"] //isNew := true vcode := params["vcode"] _, err := dal.DefaultSMSCodeDal.Get(mobile, vcode, int(constants.SMSCODE_LOGIN)) if err != nil { //return nil, errors.New("5::验证码错误") } userTemp, err := userService._registUser(mobile, false) if err != nil { return nil, errors.New("regist failed") } if userTemp != nil { //isNew = userTemp.IsNew } go func() { db := util.GetWriteSqlDB() db.Exec("update t_sms_code set is_used = 1 where mobile=? and code_type = ? and code=?", mobile, constants.SMSCODE_LOGIN, vcode) db.Exec("update t_custom set is_accept_up = 'Y' where mobile = ?;", mobile) unionId := params["unionId"] if len(unionId) > 0 { db.Exec("update t_custom set unionid = ? where mobile = ?;", unionId, mobile) } }() go func() { //20210304此处逻辑 只能插入一次t_login_coupons //如果之前下过有效订单发放 99 优惠券; //如果之前没下过单,或者单据都是在状态(7,9,14) 发放30 优惠券 db := util.GetWriteSqlDB() //todo 插入 t_login_coupons 只能注册一次 sqlResult := db.MustExec("insert into t_login_coupons (mobile,is_send) SELECT ?, 0 FROM DUAL WHERE NOT EXISTS(SELECT mobile FROM t_login_coupons WHERE mobile = ?)", mobile, mobile) if re, _ := sqlResult.RowsAffected(); re <= 0 { return } client := util.GetRedis() client.Select(12) client.HSet("coupons_unsend", mobile, time.Now().Format("2006-01-02 15:04:05")) }() /*config := nsq.NewConfig() producer, _ := nsq.NewProducer("127.0.0.1:4150", config) producer.Publish("api_logic", []byte("user "+mobile+"logged in")) */ user, _ := userService.IUser.Get(mobile) //todo save user login identifier to redis or somewhere else var client = util.GetRedis() client.Select(server.REDIS_API_AUTH_DB) sessionToken := server.GenerateSessionToken(fmt.Sprintf("%d-%s", user.Id, user.Mobile)) tokenBytes, err := json.Marshal(sessionToken) if err != nil { return nil, err } //sessionId := server.GenerateSessionId(user.Id, source) statusCmd := client.Set(sessionToken.Token, string(tokenBytes)) if err = statusCmd.Err(); err != nil { return nil, err } /*var exp int64 exp, err = config.IniConf.Section("server").Key("redis_api_session_expire").Int64() if err != nil || exp < 30 { exp = 60 } client.Expire(sessionToken.Token, time.Minute*time.Duration(exp))*/ return nil, nil } func (userService *UserService) GetHomeInvite(params map[string]string) (interface{}, error) { data, _, err := getHomeInviteByOpenid(params["toopenid"]) if err != nil { return nil, err } return data, nil } func getUserWxInfo(openid string) (interface{}, error) { var wxInfo = struct { Openid string `db:"openid" json:"openid"` Nickname string `db:"nickname" json:"nickname"` Gender int `db:"sex" json:"gender"` HeadImgUrl string `db:"headimgurl" json:"headimgurl"` Subscribe int `db:"subscribe" json:"subscribe"` }{} db := util.GetSqlDB() strSql := "select openid, nickname, sex, headimgurl,subscribe from t_wechat_userinfo where openid = ? limit 1;" err := db.Get(&wxInfo, strSql, openid) if err == sql.ErrNoRows { return nil, nil } return wxInfo, err } func (userService *UserService) GetUserWxInfo(openid string) (interface{}, error) { return getUserWxInfo(openid) } const PASSWORD_SALT_SIZE int = 6 func (userService *UserService) Regist(userName string, password string, vcode string) (interface{}, error) { if ok := util.IsMobile(userName); !ok { return nil, errors.New("1::invalid user name") } if strings.Trim(password, " ") == "" { return nil, errors.New("2::password required") } _, err := dal.DefaultSMSCodeDal.Get(userName, vcode, int(constants.SMSCODE_REGIST)) if err != nil { return nil, errors.New("4::验证码错误") } user, err := userService._registUser(userName, true) if err != nil { //return user, dal.DBRECORD_ALREADY_EXISTS_ERROR if err == errRecordExsits { return nil, errors.New("3::手机号已注册") } return nil, err } go func() { db := util.GetWriteSqlDB() db.Exec("update t_sms_code set is_used = 1 where mobile=? and code_type = ? and code=?", userName, constants.SMSCODE_REGIST, vcode) }() return user, nil } var errRecordExsits = errors.New("record already exists") func (userService *UserService) _registUser(userName string, toCheckReturn bool) (*entity.UserDB, error) { //check whether account is already regist user, err := userService.IUser.Get(userName) if err != nil && err != dal.DBRECORD_NOT_EXISTS_ERROR { return nil, err } if toCheckReturn { return nil, errRecordExsits } db := util.GetWriteSqlDB() if user != nil { if user.IsAcceptProto != "Y" { db.Exec("update t_custom set is_accept_up = 'Y' where mobile = ?;", userName) } user.IsNew = false return user, nil } var pwdSalt = util.RandNumString(PASSWORD_SALT_SIZE) ml := len(userName) var password = userName[ml-6:] password = encryptPassword(PASSWORD_KEY + password + pwdSalt) var fields = map[string]interface{}{ "mobile": userName, "password": password, "account": userName, "password_salt": pwdSalt, "is_accept_up": "Y", "status": 1, "created_at": time.Now().Format("2006-01-02 15:04:05"), } var sqlStr = util.GenerateInsertSql("t_custom", fields) sqlResult, err := db.NamedExec(sqlStr, fields) if err != nil { return nil, err } uid, _ := sqlResult.LastInsertId() return &entity.UserDB{ Id: uid, Mobile: userName, IsNew: true, }, nil } func (userService *UserService) _registUserV2(params map[string]string) (interface{}, error) { //check whether account is already regist user, err := userService.IUser.Get(params["mobile"]) if err != nil && err != dal.DBRECORD_NOT_EXISTS_ERROR { return nil, err } db := util.GetWriteSqlDB() //已经存在的用户,直接返回 if user != nil { //20230517 判断修改openid 的逻辑 if params["openid"] != "" { var extra string //更新openid //如果存在 extra 解析 json数据,更新openid //如果不存在,直接拼json后更新 if user.Extra.Valid && strings.TrimSpace(user.Extra.String) != "" { var dat map[string]interface{} if err := json.Unmarshal([]byte(user.Extra.String), &dat); err == nil { log.Println("loginV2 update exist custom's openid ", dat) dat["openid"] = params["openid"] str, err := json.Marshal(dat) if err != nil { return nil, errors.New("2::failed to extra openid parse json by loginV2") } extra = string(str) } else { return nil, errors.New("3::failed to update openid by loginV2") } } else { extra = fmt.Sprintf(`{"openid":"%s"}`, params["openid"]) } db.Exec("update t_custom set extra = ? where mobile = ?;", extra, params["mobile"]) } if user.IsAcceptProto != "Y" { db.Exec("update t_custom set is_accept_up = 'Y' where mobile = ?;", params["mobile"]) } return "N", nil } var pwdSalt = util.RandNumString(PASSWORD_SALT_SIZE) ml := len(params["mobile"]) var password = params["mobile"][ml-6:] password = encryptPassword(PASSWORD_KEY + password + pwdSalt) var fields = map[string]interface{}{ "mobile": params["mobile"], "password": password, "account": params["mobile"], "password_salt": pwdSalt, "is_accept_up": "Y", "status": 1, "extra": fmt.Sprintf(`{"openid":"%s"}`, params["openid"]), "created_at": time.Now().Format("2006-01-02 15:04:05"), } if params["openid"] == "" { //delete(fields,"extra") fields["extra"] = "" } tx := db.MustBegin() //1.新建用户数据 var sqlStr = util.GenerateInsertSql("t_custom", fields) _, err = tx.NamedExec(sqlStr, fields) if err != nil { //fmt.Println("split order ", oitem.Id, err) tx.Tx.Rollback() return nil, errors.New("4::failed to insert t_custom By loginV2") } //如果toopenid 是null 取消后续操作 if params["toopenid"] == "" { tx.Commit() return "Y", nil } //2.插入t_home_invite 关系 strSql := "insert into t_home_invite(fromopenid, frommobile, fromnickname, fromheadimg, toopenid,create_time) values (?,?,?,?,?,?)" sqlResult := tx.MustExec(strSql, params["openid"], params["mobile"], params["fromnickname"], params["fromheadimg"], params["toopenid"], time.Now().Format("2006-01-02 15:04:05")) if ra, _ := sqlResult.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return nil, errors.New("5::failed to insert t_home_invite By loginV2") } //3.统计已经存在的关系.如果大于10个,就更新t_agent _, count, err := getHomeInviteByOpenid(params["toopenid"]) if err != nil { return nil, errors.New("6::failed to insert t_home_invite By loginV2") } // if count >= 9 { //因为是事务,所以第10条数据还没有提交 user, err := userService.IUser.GetUserByOpenid(params["toopenid"]) if err != nil && err != dal.DBRECORD_NOT_EXISTS_ERROR { tx.Tx.Rollback() return nil, errors.New("7::failed to get user mobile by toopenid By loginV2") } strSql = "update t_agent set redpacket=1000 ,updatetime = ? where openid=? " updateResult := tx.MustExec(strSql, time.Now().Format("2006-01-02 15:04:05"), params["toopenid"]) if ra, _ := updateResult.RowsAffected(); ra <= 0 { strSql = "insert into t_agent(openid, mobile, updatetime,redpacket) values(?,?,?,1000)" insertResult := tx.MustExec(strSql, params["toopenid"], user.Mobile, time.Now().Format("2006-01-02 15:04:05")) if ra, _ := insertResult.RowsAffected(); ra <= 0 { tx.Tx.Rollback() return nil, errors.New("8::failed to insert/update t_agent By loginV2") } } } tx.Commit() if err != nil { return nil, err } return "Y", nil } func getHomeInviteByOpenid(openid string) (interface{}, int, error) { var homeInvite []struct { Fromopenid null.String `db:"fromopenid" json:"fromopenid"` Frommobile null.String `db:"frommobile" json:"frommobile"` Fromnickname null.String `db:"fromnickname" json:"fromnickname"` Fromheadimg null.String `db:"fromheadimg" json:"fromheadimg"` ToOpenid null.String `db:"toopenid" json:"toopenid"` CreateTime null.String `db:"create_time" json:"create_time"` } db := util.GetSqlDB() strSql := "select fromopenid, frommobile, fromnickname, fromheadimg,toopenid,create_time from t_home_invite where toopenid = ? order by create_time asc limit 10;" err := db.Select(&homeInvite, strSql, openid) if err == sql.ErrNoRows { return nil, 0, nil } return homeInvite, len(homeInvite), err } func (userService *UserService) ResetPwd(userName, password, newPassword string) (interface{}, error) { user, err := userService.IUser.Get(userName) if err != nil { return nil, err } if user == nil { return nil, errors.New("找不到用户") } password = encryptPassword(PASSWORD_KEY + password + user.PasswordSalt) if password != user.Password { return nil, errors.New("密码错误") } var pwdSalt = util.RandNumString(PASSWORD_SALT_SIZE) newPassword = encryptPassword(PASSWORD_KEY + newPassword + pwdSalt) um := entity.User{ Id: user.Id, Password: newPassword, PasswordSalt: pwdSalt, } r, err := userService.IUser.Update(&um) if err != nil { return nil, err } return r, nil } func (userService *UserService) ForgetPwd(userName, vcode string, password string) (interface{}, error) { code, err := dal.DefaultSMSCodeDal.Get(userName, vcode, int(constants.SMSCODE_FORGOT_PASSWORD)) if code == nil || code.Mobile == "" { return nil, errors.New("1::验证码错误") } var pwdSalt = util.RandNumString(PASSWORD_SALT_SIZE) password = encryptPassword(PASSWORD_KEY + password + pwdSalt) um := entity.User{ Mobile: userName, Password: password, PasswordSalt: pwdSalt, } r, err := userService.IUser.Update(&um) if err != nil { return nil, err } go func() { db := util.GetWriteSqlDB() db.Exec("update t_sms_code set is_used = 1 where mobile=? and code_type = ? and code=?", userName, constants.SMSCODE_REGIST, vcode) }() return r, nil } func (userService *UserService) SetOpenid(mobile string, openid string, wxType string) (interface{}, error) { if ok := util.IsMobile(mobile); !ok { return false, errors.New("1::invalid mobile") } openid = strings.Trim(openid, " ") if openid == "" { return false, errors.New("2::openid is empty") } user, err := userService.IUser.Get(mobile) if err != nil { return nil, err } var extra = map[string]interface{}{} if user != nil && user.Extra.Valid { json.Unmarshal([]byte(user.Extra.String), &extra) } var key = "openid" if wxType == "mp" { key = "openid_mp" } extra[key] = openid buf, _ := json.Marshal(extra) um := entity.User{ Mobile: mobile, Extra: string(buf), } _, err = userService.IUser.Update(&um) if err != nil { return false, err } return true, nil } func (userService *UserService) UpdateUserinfo(uinfo *entity.User) (interface{}, error) { toUpUinfo := entity.User{ Id: uinfo.Id, Avatar: uinfo.Avatar, } return userService.IUser.Update(&toUpUinfo) } func (userService *UserService) GetQueueNotice(mobile string) (interface{}, error) { return userService.IUser.GetNotice(mobile) } func (userService *UserService) UpdateQueueNotice(notice *entity.QueueNotice) (interface{}, error) { return userService.IUser.UpdateNotice(notice) } func (userService *UserService) GetRelationship() (interface{}, error) { relationStr := config.IniConf.Section("server").Key("user_relationship").Value() return strings.Split(relationStr, ","), nil } func encryptPassword(source string) string { dst := sha1.Sum([]byte(source)) dstStr := hex.EncodeToString(dst[0:]) return strings.ToUpper(dstStr) } func (userService *UserService) SaveInfo(uinfo *entity.User) (interface{}, error) { toUpUinfo := entity.User{ Id: uinfo.Id, Height: uinfo.Height, Weight: uinfo.Weight, Gender: uinfo.Gender, BirthDay: uinfo.BirthDay, } return userService.IUser.Update(&toUpUinfo) } func (userService *UserService) SaveHobby(uinfo *entity.User) (interface{}, error) { toUpUinfo := entity.User{ Id: uinfo.Id, Hobby: uinfo.Hobby, } return userService.IUser.Update(&toUpUinfo) } func (userService *UserService) GetHobby() (interface{}, error) { db := util.GetSqlDB() result := []struct { Id int `db:"id"` HobbyName string `db:"hobby_name"` ImgUrlSelected null.String `db:"img_url_selected"` ImgUrlUnSelected null.String `db:"img_url_unselected"` }{} err := db.Select(&result, "select * from t_custom_hobby ") if err != nil { return "", errors.New("1::search hobby error: " + err.Error()) } return result, nil }