package service import ( "context" "fmt" "go-common/app/interface/main/creative/conf" "go-common/app/interface/main/creative/dao/account" "go-common/app/interface/main/creative/dao/activity" "go-common/app/interface/main/creative/dao/archive" "go-common/app/interface/main/creative/dao/article" "go-common/app/interface/main/creative/dao/creative" "go-common/app/interface/main/creative/dao/pay" "go-common/app/interface/main/creative/dao/subtitle" "net/url" "os" "go-common/app/interface/main/creative/dao/tag" "go-common/app/interface/main/creative/dao/up" actmdl "go-common/app/interface/main/creative/model/activity" arcinter "go-common/app/interface/main/creative/model/archive" arcmdl "go-common/app/interface/main/creative/model/archive" "go-common/app/interface/main/creative/model/music" mMdl "go-common/app/interface/main/creative/model/music" "go-common/app/interface/main/creative/model/newcomer" tagMdl "go-common/app/interface/main/creative/model/tag" accMdl "go-common/app/service/main/account/model" mdlarc "go-common/app/service/main/archive/model/archive" "go-common/library/log" "go-common/library/queue/databus" "go-common/library/sync/errgroup" xtime "go-common/library/time" "go-common/library/xstr" "hash/crc32" "math" "strconv" "strings" "time" ) //Public struct type Public struct { c *conf.Config creative *creative.Dao sub *subtitle.Dao acc *account.Dao act *activity.Dao arc *archive.Dao up *up.Dao pay *pay.Dao tag *tag.Dao // type cache TypesCache map[string][]*arcmdl.Type TopTypesCache []*arcmdl.Type TypeMapCache map[int16]*arcmdl.Type CTypesCache map[string][]*arcmdl.Type AllMusics map[int64]*mMdl.Music DescFmtsCache map[int64]map[int8]map[int8]*arcmdl.DescFormat DescFmtsArrCache []*arcmdl.DescFormat // cache ActVideoAllCache []*actmdl.Activity TopActCache []*actmdl.Activity ActMapCache map[int64]*actmdl.Activity StaffTitlesCache []*tagMdl.StaffTitle //task taskPub *databus.Databus AppWhiteMidsByGroups map[int64]map[int64]int64 } //RPCDaos struct type RPCDaos struct { Arc *archive.Dao Acc *account.Dao Art *article.Dao Up *up.Dao Sub *subtitle.Dao } //New get service func New(c *conf.Config, rpcdaos *RPCDaos) *Public { p := &Public{ c: c, creative: creative.New(c), arc: archive.New(c), sub: subtitle.New(c), act: activity.New(c), pay: pay.New(c), tag: tag.New(c), acc: rpcdaos.Acc, up: rpcdaos.Up, taskPub: databus.New(c.TaskPub), AllMusics: make(map[int64]*mMdl.Music), ActVideoAllCache: make([]*actmdl.Activity, 0), TopActCache: make([]*actmdl.Activity, 0), StaffTitlesCache: make([]*tagMdl.StaffTitle, 0), ActMapCache: make(map[int64]*actmdl.Activity), AppWhiteMidsByGroups: make(map[int64]map[int64]int64), } p.loadTypes() p.loadDescFormat() p.loadMusicTable() p.loadActivities() p.loadPortalGroups() p.loadStaffTitles() go p.loadproc() go p.tableproc() return p } //loadPortalGroups fn func (p *Public) loadPortalGroups() { var ( tmpGroupMaps = make(map[int64]map[int64]int64) specialGroupIDs map[int64]int8 ) if os.Getenv("DEPLOY_ENV") == "uat" { specialGroupIDs = map[int64]int8{ 29: 1, 12: 1, } } else { specialGroupIDs = map[int64]int8{ 22: 1, // 移动端新手任务白名单 23: 1, // OPG用户组(内部人员名单 } } c := context.TODO() gps := make([]int64, 0) for gpKey := range specialGroupIDs { gps = append(gps, gpKey) } type ChData struct { gp int64 gmap map[int64]int64 } rechan := make(chan ChData, len(gps)) g, ctx := errgroup.WithContext(c) for _, gpID := range gps { var gid = gpID g.Go(func() error { ret, e := p.up.UpSpecial(ctx, gid) if e != nil { log.Warn("p.up.UpSpecial gid (%d)", gid) return nil } if len(ret) > 0 { log.Warn("len of ret gid (%d)|(%d)", gid, len(ret)) } rechan <- ChData{gid, ret} return nil }) } g.Wait() close(rechan) for c := range rechan { tmpGroupMaps[c.gp] = c.gmap } p.AppWhiteMidsByGroups = tmpGroupMaps } func (p *Public) tableproc() { for { time.Sleep(time.Duration(10 * time.Second)) p.loadMusicTable() } } func (p *Public) loadMusicTable() { var ( err error musicMap map[int64]*mMdl.Music ) c := context.TODO() if musicMap, err = p.arc.AllMusics(c); err != nil { log.Error("p.music.MCategorys err(%+v)", err) return } if musicMap != nil { p.AllMusics = musicMap } log.Info("loadMusicTable (%d)", len(p.AllMusics)) } // NewRPCDaos get all func NewRPCDaos(c *conf.Config) *RPCDaos { rds := &RPCDaos{ Arc: archive.New(c), Acc: account.New(c), Art: article.New(c), Up: up.New(c), Sub: subtitle.New(c), } return rds } // loadproc func (p *Public) loadproc() { for { time.Sleep(5 * time.Minute) p.loadTypes() p.loadDescFormat() p.loadActivities() p.loadPortalGroups() p.loadStaffTitles() } } // loadActivities fn func (p *Public) loadActivities() { p.ActVideoAllCache = make([]*actmdl.Activity, 0) videoallActs, err := p.act.Activities(context.TODO()) if err != nil { return } for _, act := range videoallActs { if len(act.Tags) == 0 { act.Tags = act.Name } else { act.Tags = strings.Split(act.Tags, ",")[0] } v := &actmdl.Activity{ ID: act.ID, Name: act.Name, Tags: act.Tags, ActURL: act.ActURL, Protocol: act.Protocol, Type: act.Type, Hot: act.Hot, STime: act.STime, } p.ActVideoAllCache = append(p.ActVideoAllCache, v) p.ActMapCache[act.ID] = v } topLen := 4 multiplier := p.c.Coefficient.ActHeat if len(p.ActVideoAllCache) <= topLen { p.TopActCache = p.ActVideoAllCache } else { p.TopActCache = p.ActVideoAllCache[:topLen] } for _, topAct := range p.TopActCache { stime, _ := time.Parse("2006-01-02 15:04:05", topAct.STime) stimeAfter3Day := stime.AddDate(0, 0, 3).Unix() if time.Now().Unix() < stimeAfter3Day { topAct.New = 1 } likeCnt, _ := p.act.Likes(context.Background(), topAct.ID) if likeCnt > 0 { topAct.Comment = fmt.Sprintf("%d人参与", int(math.Ceil(float64(likeCnt)*multiplier))) } } } //load types func (p *Public) loadTypes() { tops, langs, typeMap, err := p.creative.Types(context.TODO()) if err != nil { log.Error("p.creative.Types error(%v)", err) return } arcmdl.SortRulesForTopTypes(tops, arcmdl.WebType) p.TopTypesCache = tops for _, vals := range langs { arcmdl.SortRulesForTopTypes(vals, arcmdl.WebType) } p.TypesCache = langs p.CTypesCache = genCTypesCache(langs) p.TypeMapCache = typeMap } // 自动过滤不需要的二级分区,如果二级分区全部删除了,自动会删除对应的一级分区 func genCTypesCache(langs map[string][]*arcmdl.Type) (CTypesCache map[string][]*arcmdl.Type) { CTypesCache = make(map[string][]*arcmdl.Type) for lang, topTypes := range langs { CTypesCache[lang] = make([]*arcmdl.Type, 0) for _, topType := range topTypes { nt := &arcmdl.Type{ ID: topType.ID, Lang: topType.Lang, Parent: topType.Parent, Name: topType.Name, Desc: topType.Desc, Descapp: topType.Descapp, Count: topType.Count, Original: topType.Original, IntroCopy: topType.IntroCopy, Notice: topType.Notice, CopyRight: topType.CopyRight, Show: topType.Show, Rank: topType.Rank, Children: []*arcmdl.Type{}, } if arcmdl.ForbidTopTypesForAppAdd(topType.ID) { nt.Show = false } for _, child := range topType.Children { if arcmdl.ForbidSubTypesForAppAdd(child.ID) { continue } nt.Children = append(nt.Children, child) } if len(nt.Children) > 0 { CTypesCache[lang] = append(CTypesCache[lang], nt) } } arcmdl.SortRulesForTopTypes(CTypesCache[lang], arcmdl.AppType) } return } // CoverURL convert cover url to full url. func CoverURL(uri string) (cover string) { if uri == "" { //cover = "http://static.hdslb.com/images/transparent.gif" return } cover = uri if strings.Index(uri, "http://") == 0 { return } if len(uri) >= 10 && uri[:10] == "/templets/" { return } if strings.HasPrefix(uri, "group1") { cover = "http://i0.hdslb.com/" + uri return } if pos := strings.Index(uri, "/uploads/"); pos != -1 && (pos == 0 || pos == 3) { cover = uri[pos+8:] } cover = strings.Replace(cover, "{IMG}", "", -1) cover = "http://i" + strconv.FormatInt(int64(crc32.ChecksumIEEE([]byte(cover)))%3, 10) + ".hdslb.com" + cover return } //BatchArchives batch get archive info. func (p *Public) BatchArchives(c context.Context, mid int64, aids []int64, ip string) (avm map[int64]*arcmdl.ArcVideo, err error) { avm, err = p.arc.Views(c, mid, aids, ip) if err != nil { log.Error("p.arc.Views aids (%v), ip(%s) err(%v)", aids, ip, err) } return } func (p *Public) loadDescFormat() { fmts, err := p.arc.DescFormat(context.TODO()) if err != nil { return } fmtsArr := make([]*arcmdl.DescFormat, 0) tp := make(map[int64]map[int8]map[int8]*arcmdl.DescFormat) for _, d := range fmts { fmtsArr = append(fmtsArr, d) if _, okTp := tp[d.TypeID]; !okTp { tp[d.TypeID] = make(map[int8]map[int8]*arcmdl.DescFormat) } if _, okCp := tp[d.TypeID][d.Copyright]; !okCp { tp[d.TypeID][d.Copyright] = make(map[int8]*arcmdl.DescFormat) } if _, okCp := tp[d.TypeID][d.Copyright][d.Lang]; !okCp { tp[d.TypeID][d.Copyright][d.Lang] = &arcmdl.DescFormat{} } tp[d.TypeID][d.Copyright][d.Lang] = d } p.DescFmtsCache = tp p.DescFmtsArrCache = fmtsArr } //TaskPub fn pub task finished msg. func (p *Public) TaskPub(mid int64, from, count int) (err error) { msg := &newcomer.TaskMsg{ MID: mid, From: from, Count: int64(count), TimeStamp: time.Now().Unix(), } log.Info("task Pub mid(%d) msg(%+v)", mid, msg) if err = p.taskPub.Send(context.TODO(), strconv.FormatInt(mid, 10), msg); err != nil { log.Error("s.taskPub.Send mid(%d) error(%v)", mid, err) return } return } // StaffList fn func (p *Public) StaffList(c context.Context, aid int64, cache bool) (res []*arcinter.Staff, err error) { if cache { return p.arc.StaffData(c, aid) } if res, err = p.arc.RawStaffData(c, aid); err != nil { log.Error("s.StaffList(%d) error(%v)", aid, err) return } return } //BgmBindList fn func (p *Public) BgmBindList(c context.Context, aid, cid, mType int64, cache bool) (resOk []*arcinter.ViewBGM, err error) { var ( data *creative.BgmData newIDS, sids []int64 ret map[int64]string musics map[int64]*music.Music res []*arcinter.ViewBGM ) //无更新逻辑 注意空缓存 if data, err = p.creative.BgmData(c, aid, cid, mType, cache); err != nil || data == nil { log.Error("s.GetMaterialData(%d,%d,%d) error(%v)", aid, cid, mType, err) return } if sids, err = xstr.SplitInts(data.Data); err != nil { log.Error("s.BgmBindList(%d,%d,%d) error(%v)", aid, cid, mType, err) return } for _, sid := range sids { if sid > 0 { newIDS = append(newIDS, sid) } } if len(newIDS) < 1 { return } //all localcache musics = p.AllMusics if musics == nil { return } res = make([]*arcinter.ViewBGM, 0) resOk = make([]*arcinter.ViewBGM, 0) var mids []int64 for _, sid := range newIDS { if _, ok := musics[sid]; !ok { continue } musicData := musics[sid] newOne := &arcinter.ViewBGM{} newOne.SID = sid newOne.MID = musicData.UpMID newOne.Title = musicData.Name newOne.Author = musicData.Musicians if musicData.State == 0 { params := url.Values{} params.Set("bgm_id", strconv.FormatInt(sid, 10)) params.Set("from_aid", strconv.FormatInt(aid, 10)) params.Set("from_cid", strconv.FormatInt(cid, 10)) params.Set("from_source", "player_page") newOne.JumpURL = p.c.H5Page.Cooperate + "?" + params.Encode() } mids = append(mids, musicData.UpMID) res = append(res, newOne) } if ret, err = p.getUpNames(c, mids); err != nil { log.Error("s.BgmBindList(%d,%d,%d) get mid(%v) name error(%v)", aid, cid, mType, mids, err) err = nil } for _, v := range res { if name, ok := ret[v.MID]; ok { v.Author = name } resOk = append(resOk, v) } if len(resOk) > 5 { resOk = resOk[:5] } return } // getUpNames fn func (p *Public) getUpNames(c context.Context, mids []int64) (ret map[int64]string, err error) { var ( minfos map[int64]*accMdl.Info ) ret = make(map[int64]string) if len(mids) > 0 { minfos, err = p.acc.Infos(c, mids, "localhost") if err != nil { log.Info("minfos err mids (%+v)|err(%+v)", mids, err) return } for _, info := range minfos { ret[info.Mid] = info.Name } } return } // FillPayInfo fill pay func (p *Public) FillPayInfo(c context.Context, a *arcmdl.Archive, ugcPayCfg *conf.UgcPay, ip string) (pay *arcmdl.UgcPayInfo) { var ( err error ass *arcmdl.PayAsset registed bool ) pay = &arcmdl.UgcPayInfo{ Acts: make(map[string]*arcmdl.PayAct), } pay.Acts["edit"] = &arcmdl.PayAct{ State: 1, } pay.Acts["delete"] = &arcmdl.PayAct{ State: 1, } ass, registed, err = p.pay.Ass(c, a.Aid, ip) if err != nil { log.Error("p.pay.Ass aids (%v), ip(%s) err(%v)", a.Aid, ip, err) } pay.Asset = ass delDeadline := xtime.Time(a.PTime.Time().AddDate(0, 0, ugcPayCfg.AllowDeleteDays).Unix()) editDeadline := xtime.Time(a.PTime.Time().AddDate(0, 0, ugcPayCfg.AllowEditDays).Unix()) if !registed { pay.Acts["edit"] = &arcmdl.PayAct{ Reason: "老稿件不允许参与UGC内容付费项目中,请重新投稿", State: 0, } } else { if a.UgcPay == 1 { if a.CTime != a.PTime && xtime.Time(time.Now().Unix()) < delDeadline { pay.Acts["delete"] = &arcmdl.PayAct{ Reason: fmt.Sprintf("付费稿件必须在开放之后的第%d天才能删除", ugcPayCfg.AllowDeleteDays), State: 0, } } if a.CTime != a.PTime && a.State != mdlarc.StateForbidRecicle && xtime.Time(time.Now().Unix()) < editDeadline { pay.Acts["edit"] = &arcmdl.PayAct{ Reason: fmt.Sprintf("付费稿件必须在开放之后的第%d天才能编辑", ugcPayCfg.AllowDeleteDays), State: 0, } } } // 有注册过,但是现在已经被关闭付费标记的稿件,也允许编辑和删除 } return } // loadStaffTitles 拉取联合投稿职能列表 func (p *Public) loadStaffTitles() { var ( c = context.TODO() err error ) if p.StaffTitlesCache, err = p.tag.StaffTitleList(c); err != nil { log.Error("p.loadStaffTitles() error(%v)", err) return } }