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.
204 lines
5.6 KiB
204 lines
5.6 KiB
package dao |
|
|
|
import ( |
|
"context" |
|
"errors" |
|
"regexp" |
|
"sort" |
|
"sync" |
|
"sync/atomic" |
|
"time" |
|
|
|
"go-common/app/service/live/recommend/internal/conf" |
|
relationV1 "go-common/app/service/live/relation/api/liverpc/v1" |
|
roomV1 "go-common/app/service/live/room/api/liverpc/v1" |
|
roomV2 "go-common/app/service/live/room/api/liverpc/v2" |
|
"go-common/library/log" |
|
"go-common/library/sync/errgroup" |
|
) |
|
|
|
var roomFeatureValue atomic.Value |
|
var recDefaultRoomIds atomic.Value |
|
|
|
// StartRoomFeatureJob 更新在线房间的特征信息 |
|
func StartRoomFeatureJob(c *conf.Config) { |
|
t := time.Tick(time.Second * 30) |
|
refreshRoomFeature(context.Background(), c) |
|
for range t { |
|
refreshRoomFeature(context.Background(), c) |
|
} |
|
} |
|
|
|
func refreshRoomFeature(ctx context.Context, c *conf.Config) (err error) { |
|
n := 20 |
|
currentIds, ok := onlineRoomIdValue.Load().(map[int64]struct{}) |
|
if !ok { |
|
log.Warn("cannot load current online room ids") |
|
err = errors.New("cannot load current online room ids") |
|
return |
|
} |
|
keys := make([]int64, 0, len(currentIds)) |
|
for k := range currentIds { |
|
keys = append(keys, k) |
|
} |
|
chunkIdsArray := sliceArray(keys, n) |
|
roomFeatures := map[int64][]int64{} |
|
var lock sync.Mutex |
|
var eg errgroup.Group |
|
for _, tmp := range chunkIdsArray { |
|
chunkIds := tmp |
|
eg.Go(func() (err error) { |
|
resp, err := RoomAPI.V2Room.GetByIds(ctx, &roomV2.RoomGetByIdsReq{Ids: chunkIds}) |
|
if err != nil || resp.GetCode() != 0 { |
|
log.Error("dao.RoomAPI.V2Room.GetByIds (%v) error(%v) resp(%v)", chunkIds, err, resp) |
|
return |
|
} |
|
|
|
resp1, err1 := RoomAPI.V1RoomPendant.GetPendantByIds(ctx, &roomV1.RoomPendantGetPendantByIdsReq{Ids: chunkIds, Type: "mobile_index_badge", Position: 2}) |
|
if err1 != nil || resp1.GetCode() != 0 { |
|
log.Error("dao.RoomAPI.V1Room.GetPendantByIds (%v) error(%v) resp(%v)", chunkIds, err1, resp1) |
|
return |
|
} |
|
|
|
uids := make([]int64, 0, n) |
|
for _, r := range resp.Data { |
|
uids = append(uids, r.Uid) |
|
} |
|
resp2, err2 := RelationAPI.V1Feed.GetUserFcBatch(ctx, &relationV1.FeedGetUserFcBatchReq{Uids: uids}) |
|
if err2 != nil || resp.GetCode() != 0 { |
|
log.Error("dao.RelationAPI.V1Relation.GetUserFcBatch (%v) error(%v) resp(%v)", chunkIds, err2, resp2) |
|
return |
|
} |
|
|
|
roomPendantInfo := resp1.Data.Result |
|
fansCountInfo := resp2.Data |
|
|
|
for roomId, r := range resp.Data { |
|
cornerTag := "" |
|
fansNum := int64(0) |
|
if PendantInfo, ok := roomPendantInfo[roomId]; ok && PendantInfo != nil { |
|
cornerTag = PendantInfo.Value |
|
} |
|
if fans, ok := fansCountInfo[r.Uid]; ok { |
|
fansNum = fans.Fc |
|
} |
|
featureVector := createFeature(c, r.AreaV2Id, cornerTag, fansNum, r.Online) |
|
lock.Lock() |
|
roomFeatures[roomId] = featureVector |
|
lock.Unlock() |
|
} |
|
return |
|
}) |
|
} |
|
eg.Wait() |
|
roomFeatureValue.Store(roomFeatures) |
|
//创建默认推荐房间列表 |
|
roomScoreSlice := ScoreSlice{} |
|
for roomId, vec := range roomFeatures { |
|
featureVector := make([]int64, len(vec)) |
|
copy(featureVector, vec) |
|
featureVector[0] = 0 |
|
counter := Counter{roomId: roomId, score: calcScore(makeWeightVec(c), featureVector)} |
|
roomScoreSlice = append(roomScoreSlice, counter) |
|
} |
|
sort.Sort(roomScoreSlice) |
|
//默认的召回源 |
|
limit := 400 |
|
recDefault := make([]int64, 0, limit) |
|
for _, counter := range roomScoreSlice { |
|
limit = limit - 1 |
|
if limit < 0 { |
|
break |
|
} |
|
recDefault = append(recDefault, counter.roomId) |
|
} |
|
recDefaultRoomIds.Store(recDefault) |
|
log.Info("refreshRoomFeature success, total num:%d recDefault_num:%d, recDefault:%+v", len(roomFeatures), len(recDefault), recDefault) |
|
return |
|
} |
|
|
|
//建立房间相关的特征向量 |
|
func createFeature(c *conf.Config, areaV2Id int64, cornerTag string, fansNum int64, onlineValue int64) (featureVector []int64) { |
|
fansMilestone := c.CommonFeature.FansNum.Values |
|
onlineMilestone := c.CommonFeature.Online.Values |
|
cornerSignList := c.CommonFeature.CornerSign.Values |
|
|
|
featureVector = append(featureVector, areaV2Id) //分区id, 留待在线计算的时候替换成0,1 |
|
featureVector = append(featureVector, oneHotEncode(fansNum, fansMilestone)...) |
|
featureVector = append(featureVector, oneHotTextEncode(cornerTag, cornerSignList)...) |
|
featureVector = append(featureVector, oneHotEncode(onlineValue, onlineMilestone)...) |
|
return |
|
} |
|
|
|
//把slice按大小切成多个等大的小slice(除了最后一块) |
|
func sliceArray(arr []int64, n int) (ret [][]int64) { |
|
remainder := len(arr) % n |
|
quotient := (len(arr) - remainder) / n |
|
num := int(quotient) |
|
if remainder > 0 { |
|
num = num + 1 |
|
} |
|
ret = make([][]int64, 0, num) |
|
for i := 0; i < num; i++ { |
|
if i < num-1 { |
|
ret = append(ret, arr[n*i:n*(i+1)]) |
|
} else { |
|
ret = append(ret, arr[n*i:]) |
|
} |
|
} |
|
return |
|
} |
|
|
|
//构建0,1组成的特征向量; 如果x<0, 返回全为0的向量 |
|
func int2Slice(x int, n int) []int64 { |
|
p := make([]int64, n) |
|
if x < 0 { |
|
return p |
|
} |
|
p[x] = 1 |
|
return p |
|
} |
|
|
|
func compAndSet(value int64, vList []int64) int { |
|
place := 0 |
|
for _, v := range vList { |
|
if value < v { |
|
return place |
|
} |
|
place = place + 1 |
|
} |
|
return place |
|
} |
|
|
|
func oneHotEncode(value int64, milestone []int64) []int64 { |
|
place := compAndSet(value, milestone) |
|
return int2Slice(place, len(milestone)+1) |
|
} |
|
|
|
// textList ["", A, B ] |
|
// 如果targetText空或者没匹配到 ret[0] = 1 |
|
func oneHotTextEncode(targetText string, textList []string) (ret []int64) { |
|
place := 0 |
|
ret = make([]int64, len(textList)) |
|
if targetText == "" { |
|
ret[0] = 1 |
|
return |
|
} |
|
for i, text := range textList { |
|
if text == "" { |
|
continue |
|
} |
|
match, err := regexp.MatchString(text, targetText) |
|
if err != nil { |
|
log.Error("oneHotTextEncode regex error " + text) |
|
place = 0 |
|
break |
|
} |
|
if match { |
|
place = i |
|
break |
|
} |
|
} |
|
ret[place] = 1 |
|
return |
|
}
|
|
|