package dao import ( "context" xsql "database/sql" "encoding/json" "fmt" "go-common/app/service/bbq/user/api" "go-common/app/service/bbq/user/internal/conf" "go-common/app/service/bbq/user/internal/model" "go-common/library/cache/redis" "go-common/library/database/sql" "go-common/library/ecode" "go-common/library/log" "strconv" "strings" ) const ( _incrUserBase = "insert into user_base (`mid`,`uname`,`face`,`birthday`,`exp`,`level`,`user_type`,`complete_degree`,`sex`) values(?,?,?,?,?,?,?,?,?)" //_incrUserUname = "insert into user_base (`mid`,`uname`) values(?,?)" // TODO: 用户签名数据暂时隐藏 // _userBase = "select `mid`,`uname`,`face`,`birthday`,`exp`,`level`,`ctime`,`mtime`,`signature`,`region`,`sex`, `user_type`,`complete_degree`from user_base where `mid` in (%s)" _userBase = "select `mid`,`uname`,`face`,`birthday`,`exp`,`level`,`ctime`,`mtime`,'',`region`,`sex`, `user_type`,`complete_degree`from user_base where `mid` in (%s)" _updateUserUname = "update user_base set `uname` = ? where `mid` = ?" _selectUname = "select `mid` from user_base where `uname` = ? and `mid` != ?" _selectBZhanUpUname = "select `mid` from user_statistics_hive where `uname` = ? and `mid` != ? and `fan_total` > 10000" _updateUser = "update user_base set uname=?, face=?, birthday=?, sex=?, region=?, signature=?, complete_degree=? where mid=?" _updateUserField = "update user_base set `%s` = ? where mid = ?" ) // keyUserBase 用户基础信息缓存key func keyUserBase(mid int64) string { return fmt.Sprintf(model.CacheKeyUserBase, mid) } // RawUserBase 从数据库获取用户基础信息 func (d *Dao) RawUserBase(c context.Context, mids []int64) (res map[int64]*api.UserBase, err error) { if len(mids) == 0 { return } var midStr string for _, mid := range mids { if len(midStr) != 0 { midStr += "," } midStr += strconv.FormatInt(mid, 10) } querySQL := fmt.Sprintf(_userBase, midStr) rows, err := d.db.Query(c, querySQL) if err != nil { log.Errorv(c, log.KV("event", "mysql_query"), log.KV("error", err), log.KV("sql", querySQL)) return } defer rows.Close() for rows.Next() { userBase := new(api.UserBase) if err = rows.Scan(&userBase.Mid, &userBase.Uname, &userBase.Face, &userBase.Birthday, &userBase.Exp, &userBase.Level, &userBase.Ctime, &userBase.Mtime, &userBase.Signature, &userBase.Region, &userBase.Sex, &userBase.UserType, &userBase.CompleteDegree); err != nil { log.Errorv(c, log.KV("event", "mysql_scan"), log.KV("error", err), log.KV("sql", querySQL)) return } if res == nil { res = make(map[int64]*api.UserBase) } res[userBase.Mid] = userBase } log.Infov(c, log.KV("event", "mysql_query"), log.KV("row_num", len(res)), log.KV("sql", querySQL)) return } // CacheUserBase multi get user base from cache. func (d *Dao) CacheUserBase(c context.Context, mids []int64) (res map[int64]*api.UserBase, err error) { if res == nil { res = make(map[int64]*api.UserBase) } keys := make([]string, 0, len(mids)) keyMidMap := make(map[int64]bool, len(mids)) for _, mid := range mids { key := keyUserBase(mid) if _, exist := keyMidMap[mid]; !exist { // duplicate mid keyMidMap[mid] = true keys = append(keys, key) } } conn := d.redis.Get(c) defer conn.Close() for _, key := range keys { conn.Send("GET", key) } conn.Flush() var data []byte for i := 0; i < len(keys); i++ { if data, err = redis.Bytes(conn.Receive()); err != nil { if err == redis.ErrNil { err = nil } else { log.Errorv(c, log.KV("event", "redis_get"), log.KV("key", keys[i])) } continue } baseItem := new(api.UserBase) json.Unmarshal(data, baseItem) res[baseItem.Mid] = baseItem } log.Infov(c, log.KV("event", "redis_get"), log.KV("row_num", len(res))) return } // AddCacheUserBase 添加用户缓存 func (d *Dao) AddCacheUserBase(c context.Context, userBases map[int64]*api.UserBase) (err error) { keyValueMap := make(map[string][]byte, len(userBases)) for mid, userBase := range userBases { key := keyUserBase(mid) if _, exist := keyValueMap[key]; !exist { data, _ := json.Marshal(userBase) keyValueMap[key] = data } } conn := d.redis.Get(c) defer conn.Close() for key, value := range keyValueMap { conn.Send("SET", key, value, "EX", model.CacheExpireUserBase) } conn.Flush() for i := 0; i < len(keyValueMap); i++ { conn.Receive() } log.Infov(c, log.KV("event", "redis_set"), log.KV("row_num", len(userBases))) return } //DelCacheUserBase 删除用户缓存 func (d *Dao) DelCacheUserBase(c context.Context, mid int64) { var key = keyUserBase(mid) conn := d.redis.Get(c) defer conn.Close() conn.Do("DEL", key) } ////TxAddUserBase . //func (d *Dao) TxAddUserBase(c context.Context, tx *sql.Tx, userBase *api.UserBase) (num int64, err error) { // var res xsql.Result // if res, err = tx.Exec(_incrUserBase, userBase.Mid, userBase.Uname, userBase.Face, userBase.Birthday, userBase.Exp, userBase.Level, userBase.UserType, userBase.CompleteDegree); err != nil { // log.Error("incr user base err(%v)", err) // return // } // d.DelCacheUserBase(c, userBase.Mid) // return res.LastInsertId() //} // // UpdateUserField . func (d *Dao) UpdateUserField(c context.Context, tx *sql.Tx, mid int64, field string, f interface{}) (num int64, err error) { var res xsql.Result querySQL := fmt.Sprintf(_updateUserField, field) if res, err = tx.Exec(querySQL, f, mid); err != nil { log.Errorv(c, log.KV("log", fmt.Sprintf("update user mid(%d) field(%s) value(%v) err(%v)", mid, field, f, err))) return } log.V(1).Infow(c, "log", "update user field", "mid", mid, "field", field, "value", f) d.DelCacheUserBase(c, mid) return res.RowsAffected() } //AddUserBase . func (d *Dao) AddUserBase(c context.Context, userBase *api.UserBase) (num int64, err error) { var res xsql.Result if res, err = d.db.Exec(c, _incrUserBase, userBase.Mid, userBase.Uname, userBase.Face, userBase.Birthday, userBase.Exp, userBase.Level, userBase.UserType, userBase.CompleteDegree, userBase.Sex); err != nil { log.Error("incr user base err(%v)", err) return } d.DelCacheUserBase(c, userBase.Mid) return res.LastInsertId() } //UpdateUserBaseUname . func (d *Dao) UpdateUserBaseUname(c context.Context, mid int64, uname string) (num int64, err error) { var res xsql.Result if res, err = d.db.Exec(c, _updateUserUname, uname, mid); err != nil { log.Error("update user base uname err(%v)", err) return } d.DelCacheUserBase(c, mid) return res.RowsAffected() } // UpdateUserBase 更新用户基础信息 func (d *Dao) UpdateUserBase(c context.Context, mid int64, userBase *api.UserBase) (num int64, err error) { var res xsql.Result if res, err = d.db.Exec(c, _updateUser, userBase.Uname, userBase.Face, userBase.Birthday, userBase.Sex, userBase.Region, userBase.Signature, userBase.CompleteDegree, mid); err != nil { log.Errorv(c, log.KV("event", "mysql_update"), log.KV("mid", mid), log.KV("error", err)) return } d.DelCacheUserBase(c, mid) return res.RowsAffected() } // CheckUname 检测昵称 func (d *Dao) CheckUname(c context.Context, mid int64, uname string) (err error) { // 前缀不能为Qing_ if strings.HasPrefix(uname, "Qing_") { err = ecode.UserUnamePrefixErr return } //特殊字符 if !model.CheckUnameSpecial(uname) { err = ecode.UserUnameSpecial return } //字符长度 if !model.CheckUnameLength(uname) { err = ecode.UserUnameLength return } //bbq是否存在 tmp := int64(0) row := d.db.QueryRow(c, _selectUname, uname, mid) if err = row.Scan(&tmp); err != nil && err != sql.ErrNoRows { err = ecode.EditUserBaseErr return } if tmp != 0 { err = ecode.UserUnameExisted log.Infow(c, "log", "uname已存在", "uname", uname, "mid", mid) return } //自己b站的昵称 var userCard *model.UserCard if userCard, err = d.RawUserCard(c, mid); err != nil { err = ecode.EditUserBaseErr return } if userCard.Name == uname { return nil } //是否是万粉的昵称 row2 := d.db.QueryRow(c, _selectBZhanUpUname, uname, mid) if tmpErr := row2.Scan(&tmp); tmpErr != nil && tmpErr != sql.ErrNoRows { err = ecode.EditUserBaseErr log.V(1).Infow(c, "log", "获取B站万粉资料为空", "uname", uname) return } if tmp != 0 { err = ecode.UserUnameExisted log.Infow(c, "log", "uname命中B站万粉up主", "uname", uname, "mid", mid) return } // 昵称是否包含敏感词 level, filterErr := d.Filter(c, uname, "BBQ_account") if filterErr != nil { log.Errorv(c, log.KV("log", "filter fail")) } else if level >= 20 { err = ecode.UserUnameFilterErr log.Warnv(c, log.KV("log", fmt.Sprintf("uname filter fail: uname=%s, level=%d", uname, level))) return } // 运营不允许使用的uname列表中 if conf.UnameConf.UnameForbidden(uname) { log.Infow(c, "log", "hit fobidden uname", "uname", uname) err = ecode.UserUnameExisted return } return nil }