首页搜索策略调整
- api路由 // @Router /go/yougo/home/friend [get]
type RandomOffset map[string]int
type MatchUserArgs struct {
Profile *pb.EntityXsUserProfile
RecommendGender int
BigAreaID uint32
Rmo score.RandomOffset
}
uidsGroup, more, err = home.New(false).GetMatchUsers(r.GetCtx(), &home.MatchUserArgs{
Profile: profile,
RecommendGender: recommendGender,
BigAreaID: ub.BigareaId,
Rmo: rmo,
})
type API struct {
rds *redis.Client
Score :Api{
HashKey: "user_score_hash_group_key",
RefreshKey: "user_score_refresh_group_key",
Store: getStorage(mode),
Groups: []string{SCORE_GROUP_S, SCORE_GROUP_A, SCORE_GROUP_B, SCORE_GROUP_C, SCORE_GROUP_D, SCORE_GROUP_E, SCORE_GROUP_F, SCORE_GROUP_G, SCORE_GROUP_H, SCORE_GROUP_I},
}
}
onlineGroupMembers, onlineCount, err1 := api.getMatchMembers(ctx, &MatchMembersArgs{
MatchUserArgs: args,
Rules: rules,
Online: consts.USER_ONLINE,
TotalNum: totalNum,
})
逻辑梳理
1. 接收请求参数,进行解密到map[string]int
2. 初始化全局的各个组数据(过滤是按组来的)
3. 加载全局的组的规则数据(每个组都有自己的规则,这些规则数据在数据库,需要一次性加载到内存)
4. 优先按各个组的规则搜索在线的用户
5. 如果数据不够就从离线的用户中凑数
6. 返回结果
4.1 确认当前搜索的uid在各个组里面的百分比(也就是应该为这个uid所提供各个组里面的相互比例,很明显uid越优质,那么提供的优质组里面的目标用户就越高)
4.2 如果当前用户是 不是菲律宾国家的男性,就判断这个用户石村是 rich 或 vo标签的,最终(SCORE_GROUP_I,SCORE_GROUP_H,SCORE_GROUP_G)
4.3 如果用户还没有 sgk 就根据uid%100 去 user_score_hash_group_key 拼接成redis的key,去里面
127.0.0.1:6379> keys user_score_hash_group_key*
1) "user_score_hash_group_key_12"
2) "user_score_hash_group_key_17"
3) "user_score_hash_group_key_15"
4) "user_score_hash_group_key_85"
5) "user_score_hash_group_key_91"
6) "user_score_hash_group_key_75"
7) "user_score_hash_group_key_41"
127.0.0.1:6379> hgetall user_score_hash_group_key_69
1) "800242169"
2) "user_score_set_d_5_male_offline"
3) "810000069"
4) "user_score_set_d_5_female_offline"
5) "810000169"
6) "user_score_set_c_5_female_offline"
7) "810000269"
8) "user_score_set_d_5_female_offline"
9) "810000369"
4.4 最终根据uid找到了当前用户属于哪个级别,然后根据级别key+yougo_friend_list_group_rate (user_score_set_d_5 + yougo_friend_list_group_rate)去redis里面获取rate(这个用户的再各个组里面比例的数据), 说白了就是先想办法搞到当前uid属于哪个级别,然后级别就确定了rate比例,各个级别的比例是有api设置的
疑问:
1. 首页搜索优化 我看有个api 是用来设置 各个组的比例的 这个是用于什么场景呀? 估计是后门
2. 我看需求里面的比例跟我代码里面了解到的有点不一样,代码里面,比如
s,a,b,c,e,f,d依次取组,然后根据组的比例取用户,
但产品是 h、s(f、g)、a、b、c… 怎么感觉组都没对齐 => 但产品是 h(f、g)、s、a、b、c… 这里的f,g属于H
用户分数计算逻辑梳理
func (api *Api) UserScore(ctx context.Context, mem *Member) error {
var err error
ctxLog, b := ctx.Value(CTX_LOG_KEY).(*Logger)
defer func() {
if b && ctxLog.IsLog {
ctxLog.Logs = append(ctxLog.Logs, fmt.Sprintf("[userscore] uid = %d, mem=%+v\n", mem.UID, mem))
if err != nil {
ctxLog.Logs = append(ctxLog.Logs, err.Error())
}
}
}()
if err = api.getMemberInfo(ctx, mem); err != nil {
return gerror.Wrapf(err, fmt.Sprintf("fail to get member info [err=%+v]\n", err))
}
if mem.Gender != consts.GENDER_MALE && mem.Gender != consts.GENDER_FEMALE {
return fmt.Errorf("用户性别异常 【mem=%+v】", mem)
}
var score int
if mem.IsOperatorConfig == MEM_USER_OPERATOR_CONFIG || mem.HasVoTag || mem.HasRichTag {
score = SCORE_USER_OPERATOR_CONFIG
} else if mem.IsNew == MEM_USER_NEW {
score = SCORE_USER_NEW
} else {
score = scoresByGender(ctx, mem)
}
var scoreGroup string
if scoreGroup, err = groupByScore(mem, score); err != nil {
return gerror.Wrapf(err, fmt.Sprintf("fail to divide group [err=%v]\n", err.Error()))
}
var ub *pb.EntityXsUserBigarea
if ub, err = dao.XsUserBigarea.Get(ctx, mem.UID); err != nil {
return err
}
if ub == nil {
return gerror.Newf("[match.matchUsers] user big area record not exist [uid=%v]", mem.UID)
}
groupKey := api.Store.GenGroupKey(ctx, &StoreMember{
Gender: uint32(mem.Gender),
Online: mem.Online,
ScoreGroup: scoreGroup,
BigAreaID: ub.BigareaId,
})
if b && ctxLog.IsLog {
ctxLog.Logs = append(ctxLog.Logs, fmt.Sprintf("[userscore] uid score = %v divide group = %s, store group key = %s\n", score, scoreGroup, groupKey))
}
var preGroupKey string
if preGroupKey, err = api.Store.GetMemberGroupHash(ctx, &StoreMember{HashKey: api.hashShardKey(mem.UID), Identify: mem.UID}); err == nil && preGroupKey != "" {
if err = api.Store.DelGroupMember(ctx, &StoreMember{Group: preGroupKey, Identify: mem.UID}); err != nil {
return gerror.Wrapf(err, "fail to del group member")
}
}
if err = api.Store.SetMemberGroupHash(ctx, &StoreMember{HashKey: api.hashShardKey(mem.UID), Group: groupKey, Identify: mem.UID}); err != nil {
return gerror.Wrapf(err, "fail to set member group hash")
}
if err = api.Store.SetGroupMember(ctx, &StoreMember{
Group: groupKey,
Identify: mem.UID,
Score: float64(score),
Gender: uint32(mem.Gender),
Online: mem.Online,
IsNew: mem.IsNew,
}); err != nil {
err = gerror.Wrapf(err, "fail to set group member")
}
return err
}
MALE_RULE_OPT = []string{
RULE_RECHARGE_WEEKLY,
}
FEMALE_RULE_OPT = []string{
RULE_INCOME_WEEKLY,
}
用户身高体重等信息的表
EntityXsYougoUserProfileExtend
房间搜索
https://alloo-dev.iambanban.com/go/yougo/home/friend
https://alloo-dev.iambanban.com/go/yougo/home/search
https://alloo-dev.iambanban.com/go/room/live/searchGuess
func (s *chatRoomEsService) Open(ctx context.Context, rid, openTime uint32) error {
/Users/hugh/banban/goproject/yougo2/alo-room-server/app/service/room/busi/live_es.go
https://alpha.letsalloo.com/go/room/live/liveHistory
https://alpha.letsalloo.com/go/room/live/recommend
https://alpha.letsalloo.com/go/room/live/list
上线需要执行的脚本
./backend --env=local user setGroupRate
日志查询
1. 首页推荐是根据每个uid获取不同用户列表的一个需求
2. 每个uid在后台会定时算uid的一个得分(每天一次),存redis
3. cmd在算出uid的score之后,会继续根据score生成该uid对应的group,存redis
4. 有了1,2,3 三步的前提,那么用户在我们应用首页的时候,api是能够直接知道uid对应组是哪一个
5. 有了用户组,再根据全局设置的组里面 各个不同组里面用户的比例(这个一开始就全局设置了),去按比例获取用户已列表
6. 1,2,3是本次的需求变更点,4略微变化,5老逻辑
查看每个用户的得分
zscore user.score.v2.home.list 810000606
127.0.0.1:6379> keys user_score_set*
1) "user_score_set_c_5_male_offline"
2) "user_score_set_d_1_male_offline"
3) "user_score_set_c_5_female_offline"
4) "user_score_set_f_5_female_online"
5) "user_score_set_s_2_male_offline"
6) "user_score_set_h_1_male_offline"
7) "user_score_set_h_2_male_offline"
8) "user_score_set_d_5_female_offline"
9) "user_score_set_d_1_female_offline"
10) "user_score_set_d_2_male_offline"
11) "user_score_set_d_5_male_offline"
12) "user_score_set_d_2_female_offline"
13) "user_score_set_f_5_female_offline"
14) "user_score_set_d_5_male_online"
g.Log().Debug("msg", "UserScoreV2-score8", "uid", uid, "bigareaId", bigareaId, "sex", sex, "score", score, "isNewUser", isNewUser, "online", online, "scoreGroup", scoreGroup)
tail -f /home/log/yougo.http.stdout.log | grep HomeUserList1
tail -f /home/log/yougo.cmd.scoreV2.stdout.log | grep -E 'UserScoreV2-'
tail -f /home/log/xs/room_http.stdout.log
hgetAll 2_yougo_friend_list_group_rate
hgetAll 5_yougo_friend_list_group_rate
h5分享
h5动态详情api
curl --location --request POST '120.26.42.149/go/yougo/h5/trend/topicDetail' \
--header 'Content-Type: application/json' \
--data-raw '{
"format":"json",
"uid": 810002192,
"tpid": 1669002688728687
}'
{
"success": true,
"msg": "success",
"data": {
"uid": 810002192,
"topic_id": "1669002688728687",
"admin_status": "pending",
"app_id": 10,
"attach": [
"upload/202211/21/810002192/637af5bf2f1da.png"
],
"atype": "picture",
"authority": "",
"cmt_num": 2,
"content": "",
"country_code": "CN",
"gift_num": 0,
"giftid": 0,
"language": "zh_CN",
"latitude": "",
"like_num": 1,
"location": "武汉市",
"longitude": "",
"pron_rate": 0,
"status": "success",
"tagtype": "",
"time": 1669002688,
"video_duration": 0,
"isfollow": 0,
"name": "Isabel1",
"icon": "upload/202211/18/810002192/63774c28b79cd.png",
"sex": 2,
"age": 18,
"ttype": "user",
"has_like": 0,
"mkname": "",
"official": 0,
"in_room": 0,
"card": null,
"comments": [],
"likes": [
{
"uid": 810002183,
"name": "Phillip1",
"time": 1671350234,
"mkname": "",
"icon": "upload/202212/08/810002183/63918d1c3ae60.png",
"sex": 1,
"age": 0,
"online": 1,
"isfollow": 0
}
],
"distance": 0,
"nextCommentKey": "",
"cover": "",
"isReal": 1,
"isAuthentication": 1,
"relation": 0,
"click_num": 345,
"is_sign": false,
"is_new": false,
"label_id": 0,
"label": "",
"area_id": 5,
"nobility": null,
"tags": [],
"user_room_info": null
},
"code": "0"
}
h5房间分享api
curl --location --request POST '120.26.42.149/go/yougo/h5/room_detail' \
--header 'Content-Type: application/json' \
--data-raw '{
"format":"json",
"uid": 810002190,
"rid": 105712454
}'
{
"success": true,
"msg": "",
"data": {
"user": {
"uid": 810002190,
"name": "Otto1",
"icon": "upload/202211/18/810002190/63772bfce565e.png",
"photos": []
},
"room": {
"rid": 105712454,
"name": "Otto1的房间",
"icon": "upload/202212/08/810002190/6391c38176908.png",
"user_name": "",
"online_num": 0,
"types": ""
}
},
"code": "0"
}
curl --location --request GET '120.26.42.149/go/room/h5/recommend/list' \
--header 'Content-Type: application/json' \
--data-raw '{
"format":"json",
"uid": 810002190
}'
{
"success": true,
"msg": "",
"code": "",
"list": [
{
"rid": 105712505,
"name": "Phillip1的房间",
"icon": "upload/202212/08/810002183/63918d1c3ae60.png",
"bicon": "",
"types": "auto",
"online_num": 1,
"area": "",
"language": "zh_cn",
"tag": null,
"need_password": false,
"uid": 810002183,
"user_name": "Phillip1"
}
]
}
房间搜索
curl --location --request POST '120.26.42.149/go/room/live/searchV2' \
--header 'user-token: e000lhCfOQmUgoebS6ltl2DyQ6hvr8zXFbw2aWsVZsXkdwg3qNSykNylJ6ybSlHCadGzmr916rP4WuOw2KCepq3b1VF500kVzN1ZJyaGNQOTafozig' \
--header 'Content-Type: application/json' \
--data-raw '{
"format":"json",
"keyword": 810002183
}'
{
"success": true,
"msg": "",
"code": "",
"data": {
"user": {
"rid": 105712505,
"user": {
"uid": 810002183,
"name": "Phillip1",
"icon": "upload/202212/08/810002183/63918d1c3ae60.png",
"sex": 1,
"role": 0,
"age": 20,
"charm_level": 0,
"finance_level": 0,
"is_sign": false,
"is_new": false,
"nobility": null,
"location": "",
"nobility_icon": "",
"user_tags": [],
"alloo_uid": "110002183",
"city": "Wuhan",
"flag": 0,
"icon_frame": "",
"live_property": "",
"watch_time": "0",
"last_live_time": "0",
"seat": "",
"entranceBanner": "",
"fontColor": "",
"designation": {
"icon": ""
}
}
},
"room": {
"rid": 105712505,
"name": "Phillip1的房间",
"icon": "upload/202212/08/810002183/63918d1c3ae60.png",
"reviews": 0,
"show_tag": null,
"city": "Wuhan",
"uid": 810002183,
"user_name": "Phillip1",
"property": "vip"
}
}
}