go-common/app/admin/main/up/service/upcrmservice/up_base_info.go

445 lines
11 KiB
Go
Raw Normal View History

2019-04-22 10:49:16 +00:00
package upcrmservice
import (
"context"
"errors"
"sync"
"time"
"go-common/app/admin/main/up/dao/global"
"go-common/app/admin/main/up/model/datamodel"
"go-common/app/admin/main/up/model/upcrmmodel"
"go-common/app/admin/main/up/util"
accgrpc "go-common/app/service/main/account/api"
"go-common/library/database/elastic"
"go-common/library/log"
"go-common/library/net/metadata"
"go-common/library/sync/errgroup"
)
const (
maxSearchItemCount = 10000
maxBatchCount = 100
)
var (
//ErrTooManySearchItem too many search items, 搜索只支持返回前10000条数据
ErrTooManySearchItem = errors.New("筛选仅支持展示前1万条")
//ErrNoMid no mid
ErrNoMid = errors.New("Mid为空")
)
//UpBaseInfoQuery query
func (s *Service) UpBaseInfoQuery(context context.Context, arg *upcrmmodel.InfoQueryArgs) (result *upcrmmodel.InfoQueryResult, err error) {
var data, e = s.upBaseInfoQueryBatch(s.crmdb.QueryUpBaseInfoBatchByMid, arg.Mid)
err = e
// 没找到按照错误处理
if err != nil || len(data) == 0 {
log.Error("get from db fail, req=%+v, err=%+v", arg, err)
return
}
result = data[0]
log.Info("query base info ok, req=%+v, err=%+v", arg, result)
return
}
//QueryDbFunc query func type
type QueryDbFunc func(fields string, mid ...int64) (result []upcrmmodel.UpBaseInfo, err error)
func (s *Service) upBaseInfoQueryBatch(queryfunc QueryDbFunc, ids ...int64) (result []*upcrmmodel.InfoQueryResult, err error) {
var data, e = queryfunc("*", ids...)
err = e
if err != nil {
log.Error("get from db fail, err=%+v", err)
return
}
for _, v := range data {
var info = upcrmmodel.InfoQueryResult{}
info.CopyFromBaseInfo(v)
info.CalculateAttr()
result = append(result, &info)
}
return
}
// UpAccountInfo get account info
func (s *Service) UpAccountInfo(c context.Context, arg *upcrmmodel.InfoAccountInfoArgs) (res []*accgrpc.Info, err error) {
var (
infosReply *accgrpc.InfosReply
mids = util.ExplodeInt64(arg.Mids, ",")
)
if infosReply, err = global.GetAccClient().Infos3(c, &accgrpc.MidsReq{Mids: mids, RealIp: metadata.String(c, metadata.RemoteIP)}); err != nil {
return
}
if infosReply == nil || infosReply.Infos == nil {
return
}
for _, v := range infosReply.Infos {
res = append(res, v)
}
log.Info("query acount info ok, req=%+v, result=%+v", arg, res)
return
}
//SearchResult struct
type SearchResult struct {
AccountState int `json:"account_state"`
Activity int `json:"activity"`
Attr int `json:"attr"`
ArticleCountAccumulate int `json:"article_count_accumulate"`
ID uint32
Mid int64
}
func getAttrFormat(attrs upcrmmodel.UpAttr) (result []int) {
// 什么要shift因为es的位是从1开始的而存储的位是从0开始的
const shift = 1
if attrs.AttrVideo != 0 {
result = append(result, upcrmmodel.AttrBitVideo+shift)
}
if attrs.AttrAudio != 0 {
result = append(result, upcrmmodel.AttrBitAudio+shift)
}
if attrs.AttrArticle != 0 {
result = append(result, upcrmmodel.AttrBitArticle+shift)
}
if attrs.AttrPhoto != 0 {
result = append(result, upcrmmodel.AttrBitPhoto+shift)
}
if attrs.AttrSign != 0 {
result = append(result, upcrmmodel.AttrBitSign+shift)
}
if attrs.AttrGrowup != 0 {
result = append(result, upcrmmodel.AttrBitGrowup+shift)
}
if attrs.AttrVerify != 0 {
result = append(result, upcrmmodel.AttrBitVerify+shift)
}
return
}
func getEsCombo(attrs upcrmmodel.UpAttr) (combos []*elastic.Combo) {
const shift = 1
var attrs1, attrs2 []interface{}
var attrFlagList = getAttrFormat(attrs)
for _, v := range attrFlagList {
if _, ok := upcrmmodel.AttrGroup1[v-shift]; ok {
attrs1 = append(attrs1, v)
} else if _, ok := upcrmmodel.AttrGroup2[v-shift]; ok {
attrs2 = append(attrs2, v)
}
}
if attrs1 != nil {
var attrGroup = make(map[string][]interface{})
attrGroup["attr_format"] = attrs1
cmb := &elastic.Combo{}
cmb.ComboIn([]map[string][]interface{}{
attrGroup},
).MinIn(1).MinAll(1)
combos = append(combos, cmb)
}
if attrs2 != nil {
var attrGroup = make(map[string][]interface{})
attrGroup["attr_format"] = attrs2
cmb := &elastic.Combo{}
cmb.ComboIn([]map[string][]interface{}{
attrGroup},
).MinIn(1).MinAll(1)
combos = append(combos, cmb)
}
return
}
//UpInfoSearch info search
func (s *Service) UpInfoSearch(c context.Context, arg *upcrmmodel.InfoSearchArgs) (result upcrmmodel.InfoSearchResult, err error) {
//调用搜索的接口
var searchData esResult
searchData, err = s.searchFromEs(c, arg)
if err != nil {
log.Error("search arg=%+v, err=%+v", arg, err)
return
}
if len(searchData.Result) == 0 {
log.Info("no data return from search, just return")
return
}
var ids []int64
for _, v := range searchData.Result {
ids = append(ids, int64(v.ID))
}
result.Result, err = s.queryUpBaseInfo(c, ids...)
if err != nil {
log.Error("query up base info fail, err=%+v", err)
return
}
result.PageInfo = searchData.Page.ToPageInfo()
log.Info("res=%+v, page=%+v", searchData, result.PageInfo)
return
}
type esPage struct {
Num int `json:"num"`
Size int `json:"size"`
Total int `json:"total"`
}
//ToPageInfo cast to page info
func (e *esPage) ToPageInfo() (pageInfo upcrmmodel.PageInfo) {
if e == nil {
return
}
pageInfo.TotalCount = e.Total
pageInfo.Size = e.Size
pageInfo.Page = e.Num
return
}
type esResult struct {
Page *esPage `json:"page"`
Result []*SearchResult `json:"result"`
}
func (s *Service) searchFromEs(c context.Context, arg *upcrmmodel.InfoSearchArgs) (searchData esResult, err error) {
if arg.Page*arg.Size > maxSearchItemCount {
err = ErrTooManySearchItem
return
}
var searchSdk = elastic.NewElastic(nil)
var r = searchSdk.NewRequest("up_crm_info")
r.Pn(arg.Page).Ps(arg.Size).Index("up_base_info").
Fields("id")
if arg.Mid != 0 {
r.WhereEq("mid", arg.Mid)
} else {
if arg.AccountState != 0 {
// 字段有0值所以接口改为12表示字段的01接口的0表示没有此条件
var realArg = arg.AccountState - 1
r.WhereEq("account_state", realArg)
}
if arg.Activity != 0 {
r.WhereEq("activity", arg.Activity)
}
var startdate, _ = time.Parse(upcrmmodel.TimeFmtDate, arg.FirstDateBegin)
var enddate, _ = time.Parse(upcrmmodel.TimeFmtDate, arg.FirstDateEnd)
enddate = enddate.AddDate(0, 0, 1)
var startStr = startdate.Format(upcrmmodel.TimeFmtMysql)
var endStr = enddate.Format(upcrmmodel.TimeFmtMysql)
if arg.FirstDateBegin != "" && arg.FirstDateEnd != "" {
r.WhereRange("first_up_time", startStr, endStr, elastic.RangeScopeLcRc)
}
if arg.Order.Order == "" {
arg.Order.Order = "desc"
}
if arg.Order.Field == "" {
arg.Order.Field = "first_up_time"
arg.Order.Order = "desc"
}
var combos = getEsCombo(arg.Attrs)
r.WhereCombo(combos...)
r.Order(arg.Order.Field, arg.Order.Order)
}
err = r.Scan(c, &searchData)
return
}
func (s *Service) queryUpBaseInfo(c context.Context, ids ...int64) (result []*upcrmmodel.InfoQueryResult, err error) {
var group, ctx = errgroup.WithContext(c)
var infoData []*upcrmmodel.InfoQueryResult
group.Go(func() error {
var e error
infoData, e = s.upBaseInfoQueryBatch(s.crmdb.QueryUpBaseInfoBatchByID, ids...)
if e != nil {
err = e
log.Error("get base info fail, err=%+v", err)
}
return nil
})
var tidMap = make(map[int64]*datamodel.UpArchiveTypeData)
var mapLock sync.Mutex
for _, mid := range ids {
group.Go(func() error {
var arg = datamodel.GetUpArchiveTypeInfoArg{Mid: mid}
var tidData, e = s.dataService.GetUpArchiveTypeInfo(ctx, &arg)
if e != nil || tidData == nil {
log.Error("get up type info err, err=%v", e)
return nil
}
mapLock.Lock()
tidMap[arg.Mid] = tidData
mapLock.Unlock()
return nil
})
}
if err = group.Wait(); err != nil {
log.Error("get data error, err=%v", err)
return
}
var infoIDMap = make(map[uint32]*upcrmmodel.InfoQueryResult)
for _, v := range infoData {
infoIDMap[v.ID] = v
}
for _, v := range ids {
var info, ok = infoIDMap[uint32(v)]
if !ok {
continue
}
if typeInfo, ok := tidMap[v]; ok {
info.ActiveTid = typeInfo.Tid
info.ActiveSubtid = typeInfo.SubTid
}
result = append(result, info)
}
return
}
//QueryUpInfoWithViewerData query with view data
func (s *Service) QueryUpInfoWithViewerData(c context.Context, arg *upcrmmodel.UpInfoWithViewerArg) (result upcrmmodel.UpInfoWithViewerResult, err error) {
if arg.Page*arg.Size > maxSearchItemCount {
err = ErrTooManySearchItem
return
}
// 如果是0则默认设置所有的tag
if arg.Flag == 0 {
arg.Flag = -1
}
var mids []int64
if arg.Mids != "" {
mids = util.ExplodeInt64(arg.Mids, ",")
var midlen = len(mids)
if midlen == 0 {
err = ErrNoMid
log.Error("no mid get from mids, arg=%+v", arg)
return
}
if midlen > maxBatchCount {
mids = mids[:maxBatchCount]
}
} else {
var searchSdk = elastic.NewElastic(nil)
var r = searchSdk.NewRequest("up_crm_info")
if arg.Size > maxBatchCount {
arg.Size = maxBatchCount
}
r.Pn(arg.Page).Ps(arg.Size).Index("up_base_info").
Fields("mid").
Order(arg.Sort, arg.Order)
var searchData esResult
err = r.Scan(c, &searchData)
if err != nil {
log.Error("fail to get from search, arg=%+v", arg)
return
}
for _, v := range searchData.Result {
mids = append(mids, int64(v.Mid))
}
result.PageInfo = searchData.Page.ToPageInfo()
}
var group, ctx = errgroup.WithContext(c)
var infoData []*upcrmmodel.InfoQueryResult
var playData []*upcrmmodel.UpPlayInfo
group.Go(func() error {
if arg.Flag&upcrmmodel.FlagUpBaseData != 0 {
infoData, err = s.upBaseInfoQueryBatch(s.crmdb.QueryUpBaseInfoBatchByMid, mids...)
if err != nil {
log.Error("query up base error, err=%v", err)
return err
}
}
if arg.Flag&upcrmmodel.FlagUpPlayData != 0 {
playData, err = s.crmdb.QueryPlayInfoBatch(mids, upcrmmodel.BusinessTypeVideo)
if err != nil {
log.Error("query play info err, err=%v", err)
return err
}
}
return nil
})
var dataMap = make(map[int64]*upcrmmodel.UpInfoWithViewerData)
if arg.Flag&upcrmmodel.FlagViewData != 0 {
for _, v := range mids {
var mid = v // copy this v
group.Go(func() error {
var info, e = s.dataService.GetViewData(ctx, mid)
if e != nil {
err = e
log.Error("query up view info from hbase error, err=%v", err)
return err
}
var data = getOrCreateUpInfo(dataMap, mid)
data.ViewerBase = info.Base
data.ViewerTrend = info.Trend
data.ViewerArea = info.Area
return err
})
}
}
if err = group.Wait(); err != nil {
log.Error("get data fail, err=%v", err)
return
}
for _, baseInfo := range infoData {
var data = getOrCreateUpInfo(dataMap, baseInfo.Mid)
data.UpBaseInfo = baseInfo
}
for _, playInfo := range playData {
var data = getOrCreateUpInfo(dataMap, playInfo.Mid)
data.UpPlayInfo = playInfo
}
for _, mid := range mids {
var data, ok = dataMap[mid]
if !ok {
log.Warn("up info not found, mid=%d", mid)
continue
}
data.Mid = mid
result.Result = append(result.Result, data)
}
log.Info("query up with view ok, arg=%+v, result count=%d", arg, len(mids))
return
}
var dataMapMutex sync.Mutex
func getOrCreateUpInfo(dataMap map[int64]*upcrmmodel.UpInfoWithViewerData, mid int64) (result *upcrmmodel.UpInfoWithViewerData) {
dataMapMutex.Lock()
defer dataMapMutex.Unlock()
var ok bool
if result, ok = dataMap[mid]; !ok {
result = &upcrmmodel.UpInfoWithViewerData{}
dataMap[mid] = result
}
return result
}