package service import ( "context" "encoding/json" "go-common/app/interface/bbq/app-bbq/api/http/v1" "go-common/app/interface/bbq/app-bbq/model" "go-common/app/service/bbq/common" "go-common/library/ecode" "go-common/library/log" "github.com/json-iterator/go" ) const ( searchTypeVideo = "bbq_video" searchTypeUser = "bbq_user" suggestType = "bbq" suggestVer = "v4" searchLen = 20 ) // SearchCursor . type SearchCursor struct { Offset int64 `json:"offset"` // 默认为0,数据从1开始 } func parseSearchCursor(c context.Context, cursorPrev, cursorNext string) (cursor *SearchCursor, directionNext bool, err error) { cursor = new(SearchCursor) // 判断是向前还是向后查询 directionNext = true cursorStr := cursorNext if len(cursorNext) == 0 && len(cursorPrev) > 0 { directionNext = false cursorStr = cursorPrev } // 解析 if len(cursorStr) != 0 { var cursorData = []byte(cursorStr) err = json.Unmarshal(cursorData, &cursor) if err != nil { err = ecode.ReqParamErr return } } return } //HotWord . func (s *Service) HotWord(c context.Context, req *v1.HotWordRequest) (res *v1.HotWordResponse, err error) { res = new(v1.HotWordResponse) res.List = append(res.List, "小姐姐") res.List = append(res.List, "舞蹈") res.List = append(res.List, "MMD") return } // VideoSearch 视频搜索 func (s *Service) VideoSearch(c context.Context, arg *v1.BaseSearchReq) (res *v1.VideoSearchRes, err error) { res = new(v1.VideoSearchRes) var ( sres []*model.VideoSearchResult searchRes *model.RawSearchRes relids []int32 svids []int64 svidMap map[int64]int64 ) // 0. check // 1. 生成请求,会根据请求参数 // v2接口 reqPage := int64(1) firstItemOffset := int64(0) var cursor *SearchCursor var directionNext bool if arg.Page == 0 { cursor, directionNext, err = parseSearchCursor(c, arg.CursorPrev, arg.CursorNext) if err != nil { log.Warnw(c, "log", "parse search cursor fail", "prev", arg.CursorPrev, "next", arg.CursorNext) return } firstItemOffset = cursor.Offset - 1 if directionNext { firstItemOffset = cursor.Offset + 1 } reqPage = (firstItemOffset-1)/searchLen + 1 log.V(1).Infow(c, "log", "v2 request", "next", arg.CursorNext, "prev", arg.CursorPrev) } else { reqPage = arg.Page } var json = jsoniter.ConfigCompatibleWithStandardLibrary sp := &model.SearchBaseReq{ KeyWord: arg.Key, Type: searchTypeVideo, Page: reqPage, PageSize: searchLen, Highlight: int64(arg.Highlight), } // 相信搜索结果返回的就是searchLen searchRes, err = s.dao.SearchBBQ(c, sp) if searchRes.Res == nil { return } if err = json.Unmarshal(searchRes.Res, &sres); err != nil { return } for _, v := range sres { relids = append(relids, v.ID) } if len(relids) == 0 { log.Infov(c, log.KV("log", "search ret empty")) return } ids := s.dao.ParseRel2ID(relids) svidMap, err = s.dao.ConvID2SVID(c, ids) if err != nil { log.Error("ConvID2SVID err [%v]", err) err = nil return } for _, svid := range svidMap { svids = append(svids, svid) } svMap, _ := s.svInfos(c, svids, 0, false) for index, sinfo := range sres { id := s.dao.ParseRel2ID([]int32{sinfo.ID}) if id[0] == 0 { continue } if svid, ok := svidMap[id[0]]; ok { if sv, ok := svMap[svid]; ok { if common.IsSearchSvStateAvailable(int64(sv.State)) { r := new(v1.VideoSearchList) r.VideoResponse = *sv r.SVID = svid r.HitColumns = sinfo.HitColumns r.TitleHighlight = sinfo.Title r.Offset = (reqPage-1)*int64(searchLen) + int64(index) + 1 res.List = append(res.List, r) } else { log.Warnw(c, "log", "get error svid in search list", "svid", svid, "sv", sv) } } } } res.NumPage = searchRes.PageNum res.Page = searchRes.Page res.HasMore = false if arg.Page == 0 { if (directionNext && searchRes.PageNum > searchRes.Page) || (!directionNext && searchRes.Page > 1) { res.HasMore = true } var list []*v1.VideoSearchList if directionNext { for _, item := range res.List { if item.Offset < firstItemOffset { continue } list = append(list, item) } } else { if firstItemOffset >= int64(len(res.List)) { firstItemOffset = int64(len(res.List)) - 1 } for i := firstItemOffset - 1; i >= 0; i-- { list = append(list, res.List[i]) } } res.List = list } var cursorValue SearchCursor for _, item := range res.List { cursorValue.Offset = item.Offset data, _ := json.Marshal(cursorValue) item.CursorValue = string(data) } return } // UserSearch 用户搜索 func (s *Service) UserSearch(c context.Context, mid int64, arg *v1.BaseSearchReq) (res *v1.UserSearchRes, err error) { res = new(v1.UserSearchRes) var ( sres []*model.UserSearchResult searchRes *model.RawSearchRes mids []int64 ) // 1. 生成请求,会根据请求参数 // v2接口 reqPage := int64(1) firstItemOffset := int64(0) var cursor *SearchCursor var directionNext bool if arg.Page == 0 { cursor, directionNext, err = parseSearchCursor(c, arg.CursorPrev, arg.CursorNext) if err != nil { log.Warnw(c, "log", "parse search cursor fail", "prev", arg.CursorPrev, "next", arg.CursorNext) return } firstItemOffset = cursor.Offset - 1 if directionNext { firstItemOffset = cursor.Offset + 1 } reqPage = (firstItemOffset-1)/searchLen + 1 log.V(1).Infow(c, "log", "v2 request", "next", arg.CursorNext, "prev", arg.CursorPrev) } else { reqPage = arg.Page } sp := &model.SearchBaseReq{ KeyWord: arg.Key, Type: searchTypeUser, Page: reqPage, PageSize: searchLen, Highlight: int64(arg.Highlight), } searchRes, err = s.dao.SearchBBQ(c, sp) if searchRes.Res == nil { return } if err = json.Unmarshal(searchRes.Res, &sres); err != nil { return } for _, v := range sres { mids = append(mids, v.ID) } if len(mids) == 0 { log.Infov(c, log.KV("log", "search ret empty")) return } uMap, _ := s.dao.BatchUserInfo(c, mid, mids, false, true, true) for index, sinfo := range sres { if u, ok := uMap[sinfo.ID]; ok { r := &v1.UserSearchList{ UserInfo: *u, } st := new(v1.UserStatic) st.Fan = u.Fan st.Follow = u.Follow st.Like = u.Like st.Liked = u.Liked st.FollowState = u.FollowState r.UserStatic = st r.HitColumns = sinfo.HitColumns r.UnameHighlight = sinfo.Uname r.Offset = (reqPage-1)*int64(searchLen) + int64(index) + 1 res.List = append(res.List, r) } } res.NumPage = searchRes.PageNum res.Page = searchRes.Page res.HasMore = false if arg.Page == 0 { if (directionNext && searchRes.PageNum > searchRes.Page) || (!directionNext && searchRes.Page > 1) { res.HasMore = true } var list []*v1.UserSearchList if directionNext { for _, item := range res.List { if item.Offset < firstItemOffset { continue } list = append(list, item) } } else { if firstItemOffset >= int64(len(res.List)) { firstItemOffset = int64(len(res.List)) - 1 } for i := firstItemOffset - 1; i >= 0; i-- { list = append(list, res.List[i]) } } res.List = list } var cursorValue SearchCursor for _, item := range res.List { cursorValue.Offset = item.Offset data, _ := json.Marshal(cursorValue) item.CursorValue = string(data) } return } // BBQSug BBQSUG服务 func (s *Service) BBQSug(c context.Context, arg *v1.SugReq) (res []*v1.SugTag, err error) { var ( sres []*model.RawSugTag data json.RawMessage ) sp := &model.SugBaseReq{ Term: arg.KeyWord, SuggestType: suggestType, MainVer: suggestVer, SugNum: arg.PageSize, Highlight: int64(arg.Highlight), } data, err = s.dao.SugBBQ(c, sp) if err != nil { log.Error("s.dao.SugBBQ err[%v] data [%s]", err, string(data)) return } var json = jsoniter.ConfigCompatibleWithStandardLibrary if err = json.Unmarshal(data, &sres); err != nil { log.Error("json.Unmarshal err[%v] data [%s]", err, string(data)) return } for _, sinfo := range sres { r := new(v1.SugTag) name := sinfo.Name r.Name = name r.Value = sinfo.Value r.Type = sinfo.Type r.Ref = sinfo.Ref res = append(res, r) } return }