go-common/app/interface/main/creative/service/public.go

548 lines
14 KiB
Go
Raw Normal View History

2019-04-22 10:49:16 +00:00
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
}
}