You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
8.8 KiB
281 lines
8.8 KiB
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 |
|
}
|
|
|