go-common/app/job/main/growup/service/lottery.go
2019-04-22 18:49:16 +08:00

326 lines
7.6 KiB
Go

package service
import (
"bytes"
"context"
"sort"
"strconv"
"sync"
"time"
"go-common/app/job/main/growup/model"
"go-common/library/ecode"
"go-common/library/log"
"golang.org/x/sync/errgroup"
)
type bubbleType int
const (
_bubbleLottery bubbleType = 1
_bubbleVote bubbleType = 2
)
type bubbleMeta struct {
avid int64
aType bubbleType
}
// use var map[] + register map ???
func (s *Service) syncBubbleMetaFuncM() map[bubbleType]func(c context.Context, start, end time.Time) ([]*bubbleMeta, error) {
return map[bubbleType]func(c context.Context, start, end time.Time) ([]*bubbleMeta, error){
_bubbleLottery: s.syncLotteryAvs,
_bubbleVote: s.syncVoteBIZAvs,
}
}
func (s *Service) syncLotteryAvs(c context.Context, start, end time.Time) (data []*bubbleMeta, err error) {
var (
offset int64
hasMore = 1
avs []int64
)
for hasMore == 1 {
var info *model.LotteryRes
info, err = s.dao.GetLotteryRIDs(c, start.Unix(), end.Unix()-1, offset)
if err != nil {
log.Error("s.dao.GetLotteryRIDs error(%v)", err)
return
}
if len(info.RIDs) > 0 {
avs = append(avs, info.RIDs...)
}
hasMore = info.HasMore
offset = info.Offset
}
data = make([]*bubbleMeta, 0, len(avs))
for _, av := range avs {
data = append(data, &bubbleMeta{
avid: av,
aType: _bubbleLottery,
})
}
return
}
func (s *Service) syncVoteBIZAvs(c context.Context, start, end time.Time) (data []*bubbleMeta, err error) {
if end.After(start.AddDate(0, 6, 0)) {
err = ecode.Error(ecode.RequestErr, "同步投票视频时间区间不能超过半年")
return
}
data = make([]*bubbleMeta, 0)
var voteAVs []*model.VoteBIZArchive
voteAVs, err = s.dao.VoteBIZArchive(c, start.Unix(), end.Unix())
if err != nil {
return
}
if len(voteAVs) == 0 {
return
}
for _, av := range voteAVs {
data = append(data, &bubbleMeta{
avid: av.Aid,
aType: _bubbleVote,
})
}
return
}
func (s *Service) saveBubbleMeta(c context.Context, metas []*bubbleMeta, date time.Time) (rows int64, err error) {
return s.dao.InsertBubbleMeta(c, assembleBubbleMeta(metas, date))
}
func assembleBubbleMeta(data []*bubbleMeta, syncDate time.Time) (values string) {
var buf bytes.Buffer
for _, v := range data {
buf.WriteString("(")
buf.WriteString(strconv.FormatInt(v.avid, 10))
buf.WriteByte(',')
buf.WriteString("'" + syncDate.Format(_layout) + "'")
buf.WriteByte(',')
buf.WriteString(strconv.Itoa(int(v.aType)))
buf.WriteString(")")
buf.WriteByte(',')
}
if buf.Len() > 0 {
buf.Truncate(buf.Len() - 1)
}
values = buf.String()
buf.Reset()
return
}
// SyncIncomeBubbleMeta .
func (s *Service) SyncIncomeBubbleMeta(c context.Context, start, end time.Time, tp int) (rows int64, err error) {
syncFuncM := s.syncBubbleMetaFuncM()
syncFunc, ok := syncFuncM[bubbleType(tp)]
if !ok {
err = ecode.Errorf(ecode.ReqParamErr, "illegal bubbleType(%d)", tp)
return
}
metas, err := syncFunc(c, start, end)
if err != nil {
log.Error("s.syncVoteBIZAvs err(%v)", err)
return
}
rows, err = s.saveBubbleMeta(c, metas, time.Now())
if err != nil {
log.Error("s.saveBubbleMeta err(%v)", err)
}
return
}
// SyncIncomeBubbleMetaTask sync meta data for income bubble to growup
func (s *Service) SyncIncomeBubbleMetaTask(c context.Context, date time.Time) (err error) {
defer func() {
GetTaskService().SetTaskStatus(c, TaskBubbleMeta, date.Format(_layout), err)
}()
var (
lock sync.Mutex
group errgroup.Group
syncMetaFuncM = s.syncBubbleMetaFuncM()
start, end = date, date.AddDate(0, 0, 1)
metas []*bubbleMeta
)
for _, syncMetaFunc := range syncMetaFuncM {
group.Go(func() error {
var data []*bubbleMeta
data, err = syncMetaFunc(c, start, end)
if err != nil {
return err
}
lock.Lock()
metas = append(metas, data...)
lock.Unlock()
return nil
})
}
err = group.Wait()
if err != nil {
return
}
_, err = s.saveBubbleMeta(c, metas, date)
if err != nil {
log.Error("s.saveBubbleMeta err(%v)", err)
}
return
}
func (s *Service) avToBType(bubbleMeta map[int64][]int) map[int64]int {
var (
res = make(map[int64]int)
typeToRatio = make(map[int]float64)
chooseBType = func(bTypes []int) (bType int) {
if len(bTypes) == 1 {
return bTypes[0]
}
sort.Slice(bTypes, func(i, j int) bool {
bti, btj := bTypes[i], bTypes[j]
if typeToRatio[bti] == typeToRatio[btj] {
return bti < btj
}
return typeToRatio[bti] < typeToRatio[btj]
})
return bTypes[0]
}
)
for _, v := range s.conf.Bubble.BRatio {
typeToRatio[v.BType] = v.Ratio
}
for avID, bTypes := range bubbleMeta {
res[avID] = chooseBType(bTypes)
}
return res
}
// SnapshotBubbleIncomeTask get income lottery
func (s *Service) SnapshotBubbleIncomeTask(c context.Context, date time.Time) (err error) {
defer func() {
GetTaskService().SetTaskStatus(c, TaskSnapshotBubbleIncome, date.Format("2006-01-02"), err)
}()
err = GetTaskService().TaskReady(c, date.Format("2006-01-02"), TaskCreativeIncome)
if err != nil {
return
}
bubbleMeta, err := s.getBubbleMeta(c)
if err != nil {
log.Error("s.getBubbleAVs error(%v)", err)
return
}
avToBType := s.avToBType(bubbleMeta)
var (
eg errgroup.Group
incomeCh = make(chan []*model.IncomeInfo, 2000)
snapshotCh = make(chan []*model.IncomeInfo, 2000)
)
// get av income by date
eg.Go(func() (err error) {
defer close(incomeCh)
var from int64
for {
var infos []*model.IncomeInfo
infos, err = s.dao.GetAvIncome(c, date, from, int64(_dbLimit))
if err != nil {
log.Error("dao.GetAvIncome error(%v)", err)
return
}
if len(infos) == 0 {
break
}
incomeCh <- infos
from = infos[len(infos)-1].ID
}
return
})
eg.Go(func() (err error) {
defer close(snapshotCh)
for income := range incomeCh {
lotteryIncome := make([]*model.IncomeInfo, 0)
for _, av := range income {
if bType, ok := avToBType[av.AVID]; ok {
av.BType = bType
lotteryIncome = append(lotteryIncome, av)
}
}
if len(lotteryIncome) > 0 {
snapshotCh <- lotteryIncome
}
}
return
})
eg.Go(func() (err error) {
for income := range snapshotCh {
_, err = s.dao.InsertBubbleIncome(c, assembleLotteryIncome(income))
if err != nil {
return
}
}
return
})
if err = eg.Wait(); err != nil {
log.Error("eg.Wait error(%v)", err)
}
return
}
func (s *Service) getBubbleMeta(c context.Context) (data map[int64][]int, err error) {
var id int64
data = make(map[int64][]int)
for {
var meta map[int64][]int
meta, id, err = s.income.GetBubbleMeta(c, id, int64(_dbLimit))
if err != nil {
return
}
if len(meta) == 0 {
break
}
for avID, bTypes := range meta {
data[avID] = append(data[avID], bTypes...)
}
}
return
}
func assembleLotteryIncome(as []*model.IncomeInfo) (values string) {
var buf bytes.Buffer
for _, a := range as {
buf.WriteString("(")
buf.WriteString(strconv.FormatInt(a.AVID, 10))
buf.WriteByte(',')
buf.WriteString(strconv.FormatInt(a.MID, 10))
buf.WriteByte(',')
buf.WriteString(strconv.FormatInt(a.TagID, 10))
buf.WriteByte(',')
buf.WriteString("'" + a.UploadTime.Format("2006-01-02 15:04:05") + "'")
buf.WriteByte(',')
buf.WriteString(strconv.FormatInt(a.TotalIncome, 10))
buf.WriteByte(',')
buf.WriteString(strconv.FormatInt(a.Income, 10))
buf.WriteByte(',')
buf.WriteString(strconv.FormatInt(a.TaxMoney, 10))
buf.WriteByte(',')
buf.WriteString("'" + a.Date.Format(_layout) + "'")
buf.WriteByte(',')
buf.WriteString(strconv.FormatInt(a.BaseIncome, 10))
buf.WriteByte(',')
buf.WriteString(strconv.Itoa(a.BType))
buf.WriteString(")")
buf.WriteByte(',')
}
if buf.Len() > 0 {
buf.Truncate(buf.Len() - 1)
}
values = buf.String()
buf.Reset()
return
}